diff options
author | Herbert Eiselt <herbert.eiselt@highstreet-technologies.com> | 2019-03-28 19:00:35 +0100 |
---|---|---|
committer | Herbert Eiselt <herbert.eiselt@highstreet-technologies.com> | 2019-03-28 19:01:01 +0100 |
commit | 62e834802dae0bd15504785503060d7875c7b4ad (patch) | |
tree | 0b3d3ae0c62b279773a2aea3daebcad5f8ad78a2 /sdnr/wt/odlux/framework | |
parent | f2bb490d9c82decbdb50c1e4db1be2f34b28d097 (diff) |
Add SDN-R odlux performance
A UI displaying performance monitoring data
Change-Id: I2a9c28549aee1bcac366354c343a63f884bf09e0
Issue-ID: SDNC-585
Signed-off-by: Herbert Eiselt <herbert.eiselt@highstreet-technologies.com>
Diffstat (limited to 'sdnr/wt/odlux/framework')
-rw-r--r-- | sdnr/wt/odlux/framework/pom.xml | 40 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/components/material-table/index.tsx | 887 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/components/material-table/utilities.ts | 43 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/components/material-ui/index.ts | 2 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/components/navigationMenu.tsx | 6 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/components/titleBar.tsx | 3 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/design/default.ts | 61 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/index.html | 46 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/models/elasticSearch.ts | 3 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts | 142 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/about.tsx | 853 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/frame.tsx | 12 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/home.tsx | 27 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/login.tsx | 50 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/test.tsx | 861 |
15 files changed, 1562 insertions, 1474 deletions
diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml index 5d4eb3e6a..06142f618 100644 --- a/sdnr/wt/odlux/framework/pom.xml +++ b/sdnr/wt/odlux/framework/pom.xml @@ -1,11 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>org.onap.ccsdk.parent</groupId> <artifactId>odlparent</artifactId> <version>1.2.2-SNAPSHOT</version> - <relativePath/> + <relativePath /> </parent> <modelVersion>4.0.0</modelVersion> <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> @@ -13,6 +15,12 @@ <version>0.4.2-SNAPSHOT</version> <packaging>jar</packaging> <name>sdnr-wt-odlux-framework</name> + <properties> + <buildtime>${maven.build.timestamp}</buildtime> + <distversion>ONAP Dublin (Flourine-SR2)</distversion> + <buildno>6.aa84511(19/03/28)</buildno> + <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version> + </properties> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -74,7 +82,7 @@ </goals> <phase>initialize</phase> <configuration> - <arguments>-W add lerna</arguments> + <arguments>add lerna@3.13.1 -W --exact</arguments> <installDirectory>${project.basedir}</installDirectory> <workingDirectory>${project.basedir}/../</workingDirectory> </configuration> @@ -106,6 +114,32 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> </plugin> + <plugin> + <groupId>com.google.code.maven-replacer-plugin</groupId> + <artifactId>replacer</artifactId> + <version>1.5.2</version> + <executions> + <execution> + <id>replace version</id> + <phase>prepare-package</phase> + <goals> + <goal>replace</goal> + </goals> + </execution> + </executions> + <configuration> + <basedir>${project.build.directory}/classes/odlux</basedir> + <includes> + <include>app.js</include> + </includes> + <replacements> + <replacement> + <token>##odlux.version##</token> + <value>${odlux.version}</value> + </replacement> + </replacements> + </configuration> + </plugin> </plugins> <resources> <resource> diff --git a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx index 61a990d81..402d1007e 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx @@ -1,425 +1,464 @@ -import * as React from 'react';
-import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles';
-
-import Table from '@material-ui/core/Table';
-import TableBody from '@material-ui/core/TableBody';
-import TableCell from '@material-ui/core/TableCell';
-import TablePagination from '@material-ui/core/TablePagination';
-import TableRow from '@material-ui/core/TableRow';
-import Paper from '@material-ui/core/Paper';
-import Checkbox from '@material-ui/core/Checkbox';
-
-import { TableToolbar } from './tableToolbar';
-import { EnhancedTableHead } from './tableHead';
-import { EnhancedTableFilter } from './tableFilter';
-
-import { ColumnModel, ColumnType } from './columnModel';
-import { Omit } from '@material-ui/core';
-import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon';
-export { ColumnModel, ColumnType } from './columnModel';
-
-type propType = string | number | null | undefined | (string|number)[];
-type dataType = { [prop: string]: propType };
-type resultType<TData = dataType> = { page: number, rowCount: number, rows: TData[] };
-
-export type DataCallback<TData = dataType> = (page?: number, rowsPerPage?: number, orderBy?: string | null, order?: 'asc' | 'desc' | null, filter?: { [property: string]: string }) =>resultType<TData> | Promise<resultType<TData>>;
-
-function desc(a: dataType, b: dataType, orderBy: string) {
- if ((b[orderBy] || "") < (a[orderBy] || "") ) {
- return -1;
- }
- if ((b[orderBy] || "") > (a[orderBy] || "") ) {
- return 1;
- }
- return 0;
-}
-
-function stableSort(array: dataType[], cmp: (a: dataType, b: dataType) => number) {
- const stabilizedThis = array.map((el, index) => [el, index]) as [dataType, number][];
- stabilizedThis.sort((a, b) => {
- const order = cmp(a[0], b[0]);
- if (order !== 0) return order;
- return a[1] - b[1];
- });
- return stabilizedThis.map(el => el[0]);
-}
-
-function getSorting(order: 'asc' | 'desc' | null, orderBy: string) {
- return order === 'desc' ? (a: dataType, b: dataType) => desc(a, b, orderBy) : (a: dataType, b: dataType) => -desc(a, b, orderBy);
-}
-
-const styles = (theme: Theme) => createStyles({
- root: {
- width: '100%',
- marginTop: theme.spacing.unit * 3,
- },
- table: {
- minWidth: 1020,
- },
- tableWrapper: {
- overflowX: 'auto',
- },
-});
-
-export type MaterialTableComponentState<TData = {}> = {
- order: 'asc' | 'desc';
- orderBy: string | null;
- selected: any[] | null;
- rows: TData[];
- rowCount: number;
- page: number;
- rowsPerPage: number;
- loading: boolean;
- showFilter: boolean;
- filter: { [property: string]: string };
-};
-
-export type TableApi = { forceRefresh?: () => Promise<void> };
-
-type MaterialTableComponentBaseProps<TData> = WithStyles<typeof styles> & {
- columns: ColumnModel<TData>[];
- idProperty: keyof TData | ((data: TData) => React.Key );
- title?: string;
- enableSelection?: boolean;
- disableSorting?: boolean;
- disableFilter?: boolean;
- customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, onClick: () => void }[];
- onHandleClick?(event: React.MouseEvent<HTMLTableRowElement>, rowData: TData): void;
-};
-
-type MaterialTableComponentPropsWithRows<TData={}> = MaterialTableComponentBaseProps<TData> & { rows: TData[]; asynchronus?: boolean; };
-type MaterialTableComponentPropsWithRequestData<TData={}> = MaterialTableComponentBaseProps<TData> & { onRequestData: DataCallback; tableApi?: TableApi; };
-type MaterialTableComponentPropsWithExternalState<TData={}> = MaterialTableComponentBaseProps<TData> & MaterialTableComponentState & {
- onToggleFilter: () => void;
- onFilterChanged: (property: string, filterTerm: string) => void;
- onHandleChangePage: (page: number) => void;
- onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void;
- onHandleRequestSort: (property: string) => void;
-};
-
-type MaterialTableComponentProps<TData = {}> =
- MaterialTableComponentPropsWithRows<TData> |
- MaterialTableComponentPropsWithRequestData<TData> |
- MaterialTableComponentPropsWithExternalState<TData>;
-
-function isMaterialTableComponentPropsWithRows(props: MaterialTableComponentProps): props is MaterialTableComponentPropsWithRows {
- return (props as MaterialTableComponentPropsWithRows).rows !== undefined && (props as MaterialTableComponentPropsWithRows).rows instanceof Array;
-}
-
-function isMaterialTableComponentPropsWithRequestData(props: MaterialTableComponentProps): props is MaterialTableComponentPropsWithRequestData {
- return (props as MaterialTableComponentPropsWithRequestData).onRequestData !== undefined && (props as MaterialTableComponentPropsWithRequestData).onRequestData instanceof Function;
-}
-
-function isMaterialTableComponentPropsWithRowsAndRequestData(props: MaterialTableComponentProps): props is MaterialTableComponentPropsWithExternalState {
- const propsWithExternalState = (props as MaterialTableComponentPropsWithExternalState)
- return propsWithExternalState.onFilterChanged instanceof Function ||
- propsWithExternalState.onHandleChangePage instanceof Function ||
- propsWithExternalState.onHandleChangeRowsPerPage instanceof Function ||
- propsWithExternalState.onToggleFilter instanceof Function ||
- propsWithExternalState.onHandleRequestSort instanceof Function
-}
-
-class MaterialTableComponent<TData extends {} = {}> extends React.Component<MaterialTableComponentProps, MaterialTableComponentState> {
-
- constructor(props: MaterialTableComponentProps) {
- super(props);
-
- const page = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.page : 0;
- const rowsPerPage = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.rowsPerPage || 10 : 10;
-
- this.state = {
- filter: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.filter || {} : {},
- showFilter: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.showFilter : false,
- loading: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.loading : false,
- order: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.order : 'asc',
- orderBy: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.orderBy : null,
- selected: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.selected : null,
- rows: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) || [],
- rowCount: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.length || 0,
- page,
- rowsPerPage,
- };
-
- if (isMaterialTableComponentPropsWithRequestData(this.props)) {
- this.update();
-
- if (this.props.tableApi) {
- this.props.tableApi.forceRefresh = () => this.update();
- }
- }
- }
- render(): JSX.Element {
- const { classes, columns } = this.props;
- const { rows, rowCount, order, orderBy, selected, rowsPerPage, page, showFilter, filter } = this.state;
- const emptyRows = rowsPerPage - Math.min(rowsPerPage, rowCount - page * rowsPerPage);
- const getId = typeof this.props.idProperty !== "function" ? (data: TData) => ((data as {[key:string]: any })[this.props.idProperty as any as string] as string | number) : this.props.idProperty;
- const toggleFilter = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.onToggleFilter : () => { !this.props.disableFilter && this.setState({ showFilter: !showFilter }, this.update) }
- return (
- <Paper className={ classes.root }>
- <TableToolbar numSelected={ selected && selected.length } title={ this.props.title } customActionButtons={ this.props.customActionButtons } onExportToCsv={ this.exportToCsv }
- onToggleFilter={ toggleFilter } />
- <div className={ classes.tableWrapper }>
- <Table className={ classes.table } aria-labelledby="tableTitle">
- <EnhancedTableHead
- columns={ columns }
- numSelected={ selected && selected.length }
- order={ order }
- orderBy={ orderBy }
- onSelectAllClick={ this.handleSelectAllClick }
- onRequestSort={ this.onHandleRequestSort }
- rowCount={ rows.length }
- enableSelection={ this.props.enableSelection }
- />
- <TableBody>
- { showFilter && <EnhancedTableFilter columns={ columns } filter={ filter } onFilterChanged={ this.onFilterChanged } enableSelection={this.props.enableSelection} /> || null }
- { rows // may need ordering here
- .map((entry: TData & { [key: string]: any }) => {
- const entryId = getId(entry);
- const isSelected = this.isSelected(entryId);
- return (
- <TableRow
- hover
- onClick={ event => this.handleClick(event, entry, entryId) }
- role="checkbox"
- aria-checked={ isSelected }
- tabIndex={ -1 }
- key={ entryId }
- selected={ isSelected }
- >
- { this.props.enableSelection
- ? <TableCell padding="checkbox" style={ { width: "50px" } }>
- <Checkbox checked={ isSelected } />
- </TableCell>
- : null
- }
- {
- this.props.columns.map(
- col => {
- const style = col.width ? { width: col.width } : { };
- return (
- <TableCell key={ col.property } align={ col.type === ColumnType.numeric && !col.align ? "right": col.align } style={ style }>
- { col.type === ColumnType.custom && col.customControl
- ? <col.customControl className={col.className} style={col.style} rowData={ entry } />
- : col.type === ColumnType.boolean
- ? <span className={col.className} style={col.style}>{col.labels ? col.labels[entry[col.property] ? "true": "false"] : String(entry[col.property]) }</span>
- : <span className={col.className} style={col.style}>{String(entry[col.property])}</span>
- }
- </TableCell>
- );
- }
- )
- }
- </TableRow>
- );
- }) }
- { emptyRows > 0 && (
- <TableRow style={ { height: 49 * emptyRows } }>
- <TableCell colSpan={ this.props.columns.length } />
- </TableRow>
- ) }
- </TableBody>
- </Table>
- </div>
- <TablePagination
- rowsPerPageOptions={ [5, 10, 25] }
- component="div"
- count={ rowCount }
- rowsPerPage={ rowsPerPage }
- page={ page }
- backIconButtonProps={ {
- 'aria-label': 'Previous Page',
- } }
- nextIconButtonProps={ {
- 'aria-label': 'Next Page',
- } }
- onChangePage={ this.onHandleChangePage }
- onChangeRowsPerPage={ this.onHandleChangeRowsPerPage }
- />
- </Paper>
- );
- }
-
- static getDerivedStateFromProps(props: MaterialTableComponentProps, state: MaterialTableComponentState & { _rawRows: {}[] }): MaterialTableComponentState & { _rawRows: {}[] } {
- if (isMaterialTableComponentPropsWithRowsAndRequestData(props)) {
- return {
- ...state,
- rows: props.rows,
- rowCount: props.rowCount,
- orderBy: props.orderBy,
- order: props.order,
- filter: props.filter,
- loading: props.loading,
- showFilter: props.showFilter,
- page: props.page,
- rowsPerPage: props.rowsPerPage
- }
- } else if (isMaterialTableComponentPropsWithRows(props) && props.asynchronus && state._rawRows !== props.rows) {
- const newState = MaterialTableComponent.updateRows(props, state);
- return {
- ...state,
- ...newState,
- _rawRows: props.rows || []
- };
- }
- return state;
- }
-
- private static updateRows(props: MaterialTableComponentPropsWithRows, state: MaterialTableComponentState): { rows: {}[], rowCount: number } {
- try {
- const { page, rowsPerPage, order, orderBy, filter } = state;
- let data: dataType[] = props.rows || [];
- let filtered = false;
- if (state.showFilter) {
- Object.keys(filter).forEach(prop => {
- const exp = filter[prop];
- filtered = filtered || exp !== undefined;
- data = exp !== undefined ? data.filter((val) => {
- const value = val[prop];
- return (value == exp) || (value && value.toString().indexOf(String(exp)) > -1);
- }) : data;
- });
- }
-
- const rowCount = data.length;
-
- data = (orderBy && order
- ? stableSort(data, getSorting(order, orderBy))
- : data).slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
-
- return {
- rows: data,
- rowCount
- };
- } catch{
- return {
- rows: [],
- rowCount: 0
- }
- }
- }
-
- private async update() {
- if (isMaterialTableComponentPropsWithRequestData(this.props)) {
- const response = await Promise.resolve(
- this.props.onRequestData(
- this.state.page, this.state.rowsPerPage, this.state.orderBy, this.state.order, this.state.showFilter && this.state.filter || {})
- );
- this.setState(response);
- } else {
- this.setState(MaterialTableComponent.updateRows(this.props, this.state));
- }
- }
-
- private onFilterChanged = (property: string, filterTerm: string) => {
- if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
- this.props.onFilterChanged(property, filterTerm);
- return;
- }
- if (this.props.disableFilter) return;
- const colDefinition = this.props.columns && this.props.columns.find(col => col.property === property);
- if (colDefinition && colDefinition.disableFilter) return;
-
- const filter = { ...this.state.filter, [property]: filterTerm };
- this.setState({
- filter
- }, this.update);
- };
-
- private onHandleRequestSort = (event: React.SyntheticEvent, property: string) => {
- if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
- this.props.onHandleRequestSort(property);
- return;
- }
- if (this.props.disableSorting) return;
- const colDefinition = this.props.columns && this.props.columns.find(col => col.property === property);
- if (colDefinition && colDefinition.disableSorting) return;
-
- const orderBy = this.state.orderBy === property && this.state.order === 'desc' ? null : property;
- const order = this.state.orderBy === property && this.state.order === 'asc' ? 'desc' : 'asc';
- this.setState({
- order,
- orderBy
- }, this.update);
- };
-
- handleSelectAllClick: () => {};
-
- private onHandleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, page: number) => {
- if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
- this.props.onHandleChangePage(page);
- return;
- }
- this.setState({
- page
- }, this.update);
- };
-
- private onHandleChangeRowsPerPage = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
- if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
- this.props.onHandleChangeRowsPerPage(+(event && event.target.value));
- return;
- }
- const rowsPerPage = +(event && event.target.value);
- if (rowsPerPage && rowsPerPage > 0) {
- this.setState({
- rowsPerPage
- }, this.update);
- }
- };
-
- private isSelected(id: string | number): boolean {
- let selected = this.state.selected || [];
- const selectedIndex = selected.indexOf(id);
- return (selectedIndex > -1);
- }
-
- private handleClick(event: React.MouseEvent<HTMLTableRowElement>, rowData: TData, id: string | number): void {
- if (this.props.onHandleClick instanceof Function) {
- this.props.onHandleClick(event, rowData);
- return;
- }
- if (!this.props.enableSelection){
- return;
- }
- let selected = this.state.selected || [];
- const selectedIndex = selected.indexOf(id);
- if (selectedIndex > -1) {
- selected = [
- ...selected.slice(0, selectedIndex),
- ...selected.slice(selectedIndex + 1)
- ];
- } else {
- selected = [
- ...selected,
- id
- ];
- }
- this.setState({
- selected
- });
- }
-
- private exportToCsv = () => {
- let file;
- const data: string[] = [];
- data.push(this.props.columns.map(col => col.title || col.property).join(',')+"\r\n");
- this.state.rows && this.state.rows.forEach((row : any)=> {
- data.push(this.props.columns.map(col => row[col.property]).join(',') + "\r\n");
- });
- const properties = { type: 'text/csv' }; // Specify the file's mime-type.
- try {
- // Specify the filename using the File constructor, but ...
- file = new File(data, "export.csv", properties);
- } catch (e) {
- // ... fall back to the Blob constructor if that isn't supported.
- file = new Blob(data, properties);
- }
- const url = URL.createObjectURL(file);
- window.location.replace(url);
- }
-}
-
-export type MaterialTableCtorType<TData extends {} = {}> = new () => React.Component<Omit<MaterialTableComponentProps<TData>, 'classes'>>;
-
-export const MaterialTable = withStyles(styles)(MaterialTableComponent);
+import * as React from 'react'; +import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles'; + +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TablePagination from '@material-ui/core/TablePagination'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; +import Checkbox from '@material-ui/core/Checkbox'; + +import { TableToolbar } from './tableToolbar'; +import { EnhancedTableHead } from './tableHead'; +import { EnhancedTableFilter } from './tableFilter'; + +import { ColumnModel, ColumnType } from './columnModel'; +import { Omit } from '@material-ui/core'; +import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon'; +export { ColumnModel, ColumnType } from './columnModel'; + +type propType = string | number | null | undefined | (string|number)[]; +type dataType = { [prop: string]: propType }; +type resultType<TData = dataType> = { page: number, rowCount: number, rows: TData[] }; + +export type DataCallback<TData = dataType> = (page?: number, rowsPerPage?: number, orderBy?: string | null, order?: 'asc' | 'desc' | null, filter?: { [property: string]: string }) =>resultType<TData> | Promise<resultType<TData>>; + +function desc(a: dataType, b: dataType, orderBy: string) { + if ((b[orderBy] || "") < (a[orderBy] || "") ) { + return -1; + } + if ((b[orderBy] || "") > (a[orderBy] || "") ) { + return 1; + } + return 0; +} + +function stableSort(array: dataType[], cmp: (a: dataType, b: dataType) => number) { + const stabilizedThis = array.map((el, index) => [el, index]) as [dataType, number][]; + stabilizedThis.sort((a, b) => { + const order = cmp(a[0], b[0]); + if (order !== 0) return order; + return a[1] - b[1]; + }); + return stabilizedThis.map(el => el[0]); +} + +function getSorting(order: 'asc' | 'desc' | null, orderBy: string) { + return order === 'desc' ? (a: dataType, b: dataType) => desc(a, b, orderBy) : (a: dataType, b: dataType) => -desc(a, b, orderBy); +} + +const styles = (theme: Theme) => createStyles({ + root: { + width: '100%', + marginTop: theme.spacing.unit * 3, + }, + table: { + minWidth: 1020, + }, + tableWrapper: { + overflowX: 'auto', + }, +}); + +export type MaterialTableComponentState<TData = {}> = { + order: 'asc' | 'desc'; + orderBy: string | null; + selected: any[] | null; + rows: TData[]; + rowCount: number; + page: number; + rowsPerPage: number; + loading: boolean; + showFilter: boolean; + filter: { [property: string]: string }; +}; + +export type TableApi = { forceRefresh?: () => Promise<void> }; + +type MaterialTableComponentBaseProps<TData> = WithStyles<typeof styles> & { + columns: ColumnModel<TData>[]; + idProperty: keyof TData | ((data: TData) => React.Key ); + title?: string; + enableSelection?: boolean; + disableSorting?: boolean; + disableFilter?: boolean; + customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, onClick: () => void }[]; + onHandleClick?(event: React.MouseEvent<HTMLTableRowElement>, rowData: TData): void; +}; + +type MaterialTableComponentPropsWithRows<TData={}> = MaterialTableComponentBaseProps<TData> & { rows: TData[]; asynchronus?: boolean; }; +type MaterialTableComponentPropsWithRequestData<TData={}> = MaterialTableComponentBaseProps<TData> & { onRequestData: DataCallback; tableApi?: TableApi; }; +type MaterialTableComponentPropsWithExternalState<TData={}> = MaterialTableComponentBaseProps<TData> & MaterialTableComponentState & { + onToggleFilter: () => void; + onFilterChanged: (property: string, filterTerm: string) => void; + onHandleChangePage: (page: number) => void; + onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void; + onHandleRequestSort: (property: string) => void; +}; + +type MaterialTableComponentProps<TData = {}> = + MaterialTableComponentPropsWithRows<TData> | + MaterialTableComponentPropsWithRequestData<TData> | + MaterialTableComponentPropsWithExternalState<TData>; + +function isMaterialTableComponentPropsWithRows(props: MaterialTableComponentProps): props is MaterialTableComponentPropsWithRows { + return (props as MaterialTableComponentPropsWithRows).rows !== undefined && (props as MaterialTableComponentPropsWithRows).rows instanceof Array; +} + +function isMaterialTableComponentPropsWithRequestData(props: MaterialTableComponentProps): props is MaterialTableComponentPropsWithRequestData { + return (props as MaterialTableComponentPropsWithRequestData).onRequestData !== undefined && (props as MaterialTableComponentPropsWithRequestData).onRequestData instanceof Function; +} + +function isMaterialTableComponentPropsWithRowsAndRequestData(props: MaterialTableComponentProps): props is MaterialTableComponentPropsWithExternalState { + const propsWithExternalState = (props as MaterialTableComponentPropsWithExternalState) + return propsWithExternalState.onFilterChanged instanceof Function || + propsWithExternalState.onHandleChangePage instanceof Function || + propsWithExternalState.onHandleChangeRowsPerPage instanceof Function || + propsWithExternalState.onToggleFilter instanceof Function || + propsWithExternalState.onHandleRequestSort instanceof Function +} + +class MaterialTableComponent<TData extends {} = {}> extends React.Component<MaterialTableComponentProps, MaterialTableComponentState> { + + constructor(props: MaterialTableComponentProps) { + super(props); + + const page = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.page : 0; + const rowsPerPage = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.rowsPerPage || 10 : 10; + + this.state = { + filter: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.filter || {} : {}, + showFilter: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.showFilter : false, + loading: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.loading : false, + order: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.order : 'asc', + orderBy: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.orderBy : null, + selected: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.selected : null, + rows: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) || [], + rowCount: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.length || 0, + page, + rowsPerPage, + }; + + if (isMaterialTableComponentPropsWithRequestData(this.props)) { + this.update(); + + if (this.props.tableApi) { + this.props.tableApi.forceRefresh = () => this.update(); + } + } + } + render(): JSX.Element { + const { classes, columns } = this.props; + const { rows, rowCount, order, orderBy, selected, rowsPerPage, page, showFilter, filter } = this.state; + const emptyRows = rowsPerPage - Math.min(rowsPerPage, rowCount - page * rowsPerPage); + const getId = typeof this.props.idProperty !== "function" ? (data: TData) => ((data as {[key:string]: any })[this.props.idProperty as any as string] as string | number) : this.props.idProperty; + const toggleFilter = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.onToggleFilter : () => { !this.props.disableFilter && this.setState({ showFilter: !showFilter }, this.update) } + return ( + <Paper className={ classes.root }> + <TableToolbar numSelected={ selected && selected.length } title={ this.props.title } customActionButtons={ this.props.customActionButtons } onExportToCsv={ this.exportToCsv } + onToggleFilter={ toggleFilter } /> + <div className={ classes.tableWrapper }> + <Table className={ classes.table } aria-labelledby="tableTitle"> + <EnhancedTableHead + columns={ columns } + numSelected={ selected && selected.length } + order={ order } + orderBy={ orderBy } + onSelectAllClick={ this.handleSelectAllClick } + onRequestSort={ this.onHandleRequestSort } + rowCount={ rows.length } + enableSelection={ this.props.enableSelection } + /> + <TableBody> + { showFilter && <EnhancedTableFilter columns={ columns } filter={ filter } onFilterChanged={ this.onFilterChanged } enableSelection={this.props.enableSelection} /> || null } + { rows // may need ordering here + .map((entry: TData & { [key: string]: any }) => { + const entryId = getId(entry); + const isSelected = this.isSelected(entryId); + return ( + <TableRow + hover + onClick={ event => this.handleClick(event, entry, entryId) } + role="checkbox" + aria-checked={ isSelected } + tabIndex={ -1 } + key={ entryId } + selected={ isSelected } + > + { this.props.enableSelection + ? <TableCell padding="checkbox" style={ { width: "50px" } }> + <Checkbox checked={ isSelected } /> + </TableCell> + : null + } + { + this.props.columns.map( + col => { + const style = col.width ? { width: col.width } : { }; + return ( + <TableCell key={ col.property } align={ col.type === ColumnType.numeric && !col.align ? "right": col.align } style={ style }> + { col.type === ColumnType.custom && col.customControl + ? <col.customControl className={col.className} style={col.style} rowData={ entry } /> + : col.type === ColumnType.boolean + ? <span className={col.className} style={col.style}>{col.labels ? col.labels[entry[col.property] ? "true": "false"] : String(entry[col.property]) }</span> + : <span className={col.className} style={col.style}>{String(entry[col.property])}</span> + } + </TableCell> + ); + } + ) + } + </TableRow> + ); + }) } + { emptyRows > 0 && ( + <TableRow style={ { height: 49 * emptyRows } }> + <TableCell colSpan={ this.props.columns.length } /> + </TableRow> + ) } + </TableBody> + </Table> + </div> + <TablePagination + rowsPerPageOptions={[5, 10, 20, 50] } + component="div" + count={ rowCount } + rowsPerPage={ rowsPerPage } + page={ page } + backIconButtonProps={ { + 'aria-label': 'Previous Page', + } } + nextIconButtonProps={ { + 'aria-label': 'Next Page', + } } + onChangePage={ this.onHandleChangePage } + onChangeRowsPerPage={ this.onHandleChangeRowsPerPage } + /> + </Paper> + ); + } + + static getDerivedStateFromProps(props: MaterialTableComponentProps, state: MaterialTableComponentState & { _rawRows: {}[] }): MaterialTableComponentState & { _rawRows: {}[] } { + if (isMaterialTableComponentPropsWithRowsAndRequestData(props)) { + return { + ...state, + rows: props.rows, + rowCount: props.rowCount, + orderBy: props.orderBy, + order: props.order, + filter: props.filter, + loading: props.loading, + showFilter: props.showFilter, + page: props.page, + rowsPerPage: props.rowsPerPage + } + } else if (isMaterialTableComponentPropsWithRows(props) && props.asynchronus && state._rawRows !== props.rows) { + const newState = MaterialTableComponent.updateRows(props, state); + return { + ...state, + ...newState, + _rawRows: props.rows || [] + }; + } + return state; + } + + private static updateRows(props: MaterialTableComponentPropsWithRows, state: MaterialTableComponentState): { rows: {}[], rowCount: number } { + try { + const { page, rowsPerPage, order, orderBy, filter } = state; + let data: dataType[] = props.rows || []; + let filtered = false; + if (state.showFilter) { + Object.keys(filter).forEach(prop => { + const exp = filter[prop]; + filtered = filtered || exp !== undefined; + data = exp !== undefined ? data.filter((val) => { + const value = val[prop]; + return (value == exp) || (value && value.toString().indexOf(String(exp)) > -1); + }) : data; + }); + } + + const rowCount = data.length; + + data = (orderBy && order + ? stableSort(data, getSorting(order, orderBy)) + : data).slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); + + return { + rows: data, + rowCount + }; + } catch{ + return { + rows: [], + rowCount: 0 + } + } + } + + private async update() { + if (isMaterialTableComponentPropsWithRequestData(this.props)) { + const response = await Promise.resolve( + this.props.onRequestData( + this.state.page, this.state.rowsPerPage, this.state.orderBy, this.state.order, this.state.showFilter && this.state.filter || {}) + ); + this.setState(response); + } else { + this.setState(MaterialTableComponent.updateRows(this.props, this.state)); + } + } + + private onFilterChanged = (property: string, filterTerm: string) => { + if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) { + this.props.onFilterChanged(property, filterTerm); + return; + } + if (this.props.disableFilter) return; + const colDefinition = this.props.columns && this.props.columns.find(col => col.property === property); + if (colDefinition && colDefinition.disableFilter) return; + + const filter = { ...this.state.filter, [property]: filterTerm }; + this.setState({ + filter + }, this.update); + }; + + private onHandleRequestSort = (event: React.SyntheticEvent, property: string) => { + if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) { + this.props.onHandleRequestSort(property); + return; + } + if (this.props.disableSorting) return; + const colDefinition = this.props.columns && this.props.columns.find(col => col.property === property); + if (colDefinition && colDefinition.disableSorting) return; + + const orderBy = this.state.orderBy === property && this.state.order === 'desc' ? null : property; + const order = this.state.orderBy === property && this.state.order === 'asc' ? 'desc' : 'asc'; + this.setState({ + order, + orderBy + }, this.update); + }; + + handleSelectAllClick: () => {}; + + private onHandleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, page: number) => { + if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) { + this.props.onHandleChangePage(page); + return; + } + this.setState({ + page + }, this.update); + }; + + private onHandleChangeRowsPerPage = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => { + if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) { + this.props.onHandleChangeRowsPerPage(+(event && event.target.value)); + return; + } + const rowsPerPage = +(event && event.target.value); + if (rowsPerPage && rowsPerPage > 0) { + this.setState({ + rowsPerPage + }, this.update); + } + }; + + private isSelected(id: string | number): boolean { + let selected = this.state.selected || []; + const selectedIndex = selected.indexOf(id); + return (selectedIndex > -1); + } + + private handleClick(event: React.MouseEvent<HTMLTableRowElement>, rowData: TData, id: string | number): void { + if (this.props.onHandleClick instanceof Function) { + this.props.onHandleClick(event, rowData); + return; + } + if (!this.props.enableSelection){ + return; + } + let selected = this.state.selected || []; + const selectedIndex = selected.indexOf(id); + if (selectedIndex > -1) { + selected = [ + ...selected.slice(0, selectedIndex), + ...selected.slice(selectedIndex + 1) + ]; + } else { + selected = [ + ...selected, + id + ]; + } + this.setState({ + selected + }); + } + + private exportToCsv = async () => { + let file; + let data: dataType[] | null = null; + let csv: string[] = []; + + + if (isMaterialTableComponentPropsWithRequestData(this.props)) { + this.setState({ loading: true }); + const result = await Promise.resolve( + this.props.onRequestData( 1, 1000, this.state.orderBy, this.state.order, this.state.showFilter && this.state.filter || {}) + ); + data = result.rows; + this.setState({ loading: true }); + } else { + data = MaterialTableComponent.updateRows(this.props, this.state).rows; + } + + if (data && data.length > 0) { + csv.push(this.props.columns.map(col => col.title || col.property).join(',') + "\r\n"); + this.state.rows && this.state.rows.forEach((row: any) => { + csv.push(this.props.columns.map(col => row[col.property]).join(',') + "\r\n"); + }); + const properties = { type: "text/csv;charset=utf-8" }; // Specify the file's mime-type. + try { + // Specify the filename using the File constructor, but ... + file = new File(csv, "export.csv", properties); + } catch (e) { + // ... fall back to the Blob constructor if that isn't supported. + file = new Blob(csv, properties); + } + } + if (!file) return; + var reader = new FileReader(); + reader.onload = function (e) { + const dataUri = reader.result as any; + const link = document.createElement("a"); + if (typeof link.download === 'string') { + link.href = dataUri; + link.download = "export.csv"; + + //Firefox requires the link to be in the body + document.body.appendChild(link); + + //simulate click + link.click(); + + //remove the link when done + document.body.removeChild(link); + } else { + window.open(dataUri); + } + } + reader.readAsDataURL(file); + + // const url = URL.createObjectURL(file); + // window.location.replace(url); + } +} + +export type MaterialTableCtorType<TData extends {} = {}> = new () => React.Component<Omit<MaterialTableComponentProps<TData>, 'classes'>>; + +export const MaterialTable = withStyles(styles)(MaterialTableComponent); export default MaterialTable;
\ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts index e52fdb731..3d6326349 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts +++ b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts @@ -16,6 +16,7 @@ export interface IExternalTableState<TData> { loading: boolean; showFilter: boolean; filter: { [property: string]: string }; + preFilter: { [property: string]: string }; } /** Create an actionHandler and actions for external table states. */ @@ -24,7 +25,7 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState //#region Actions abstract class TableAction extends Action { } - + class RequestSortAction extends TableAction { constructor(public orderBy: string) { super(); @@ -49,8 +50,14 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState } } + class SetPreFilterChangedAction extends TableAction { + constructor(public preFilter: {[key: string]: string}) { + super(); + } + } + class SetFilterChangedAction extends TableAction { - constructor(public filter: {[key: string]: string}) { + constructor (public filter: { [key: string]: string }) { super(); } } @@ -74,7 +81,7 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState } // #endregion - + //#region Action Handler const externalTableStateInit: IExternalTableState<TData> = { order: 'asc', @@ -86,7 +93,8 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState rowsPerPage: 10, loading: false, showFilter: false, - filter: {} + filter: {}, + preFilter: {} }; const externalTableStateActionHandler: IActionHandler<IExternalTableState<TData>> = (state = externalTableStateInit, action) => { @@ -117,6 +125,12 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState loading: true, showFilter: action.show } + } else if (action instanceof SetPreFilterChangedAction) { + state = { + ...state, + loading: true, + preFilter: action.preFilter + } } else if (action instanceof SetFilterChangedAction) { state = { ...state, @@ -135,7 +149,7 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState loading: true, rowsPerPage: action.rowsPerPage } - } + } return state; } @@ -145,11 +159,21 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState const reloadAction = (dispatch: Dispatch, getAppState: () => IApplicationStoreState) => { dispatch(new RefreshAction()); const ownState = selectState(getAppState()); - Promise.resolve(callback(ownState.page, ownState.rowsPerPage, ownState.orderBy, ownState.order, ownState.showFilter && ownState.filter || {})).then(result => { + const filter = { ...ownState.preFilter, ...(ownState.showFilter && ownState.filter || {})}; + Promise.resolve(callback(ownState.page, ownState.rowsPerPage, ownState.orderBy, ownState.order, filter )).then(result => { dispatch(new SetResultAction(result)); }).catch(error => new AddErrorInfoAction(error)); }; + const createPreActions = (dispatch: Dispatch, skipRefresh: boolean = false) => { + return { + onPreFilterChanged: (preFilter: { [key: string]: string }) => { + dispatch(new SetPreFilterChangedAction(preFilter)); + (!skipRefresh) && dispatch(reloadAction); + } + }; + } + const createActions = (dispatch: Dispatch, skipRefresh: boolean = false) => { return { onRefresh: () => { @@ -176,7 +200,7 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState (!skipRefresh) && dispatch(reloadAction); }); }, - onHandleChangePage: (page: number) => { + onHandleChangePage: (page: number) => { dispatch((dispatch: Dispatch) => { dispatch(new SetPageAction(page)); (!skipRefresh) && dispatch(reloadAction); @@ -195,13 +219,14 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState const createProperties = (state: IApplicationStoreState) => { return { ...selectState(state) - } + } } - + return { reloadAction: reloadAction, createActions: createActions, createProperties: createProperties, + createPreActions: createPreActions, actionHandler: externalTableStateActionHandler } }
\ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/index.ts b/sdnr/wt/odlux/framework/src/components/material-ui/index.ts index 890312ce2..287c018b6 100644 --- a/sdnr/wt/odlux/framework/src/components/material-ui/index.ts +++ b/sdnr/wt/odlux/framework/src/components/material-ui/index.ts @@ -1,3 +1,5 @@ export { ListItemLink } from './listItemLink';
export { Panel } from './panel';
export { ToggleButton, ToggleButtonClassKey } from './toggleButton';
+export { TreeView, ITreeItem, TreeViewCtorType} from './treeView';
+
diff --git a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx index 3a7725b1b..3da5b2f8a 100644 --- a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx +++ b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx @@ -37,8 +37,8 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state }: <div className={classes.toolbar} />
{ /* https://fiffty.github.io/react-treeview-mui/ */}
<List component="nav">
- { process.env.NODE_ENV === "development" ? <ListItemLink exact to="/" primary="Home" icon={<FontAwesomeIcon icon={faHome} />} /> : null }
- <Divider />
+ <ListItemLink exact to="/" primary="Home" icon={<FontAwesomeIcon icon={faHome} />} />
+ <Divider />
{
state.framework.applicationRegistraion && Object.keys(state.framework.applicationRegistraion).map(key => {
const reg = state.framework.applicationRegistraion[key];
@@ -53,7 +53,7 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state }: }) || null
}
<Divider />
- { process.env.NODE_ENV === "development" ? <ListItemLink to="/about" primary="About" icon={<FontAwesomeIcon icon={faAddressBook} />} /> : null }
+ <ListItemLink to="/about" primary="About" icon={<FontAwesomeIcon icon={faAddressBook} />} />
</List>
</> || null
}
diff --git a/sdnr/wt/odlux/framework/src/components/titleBar.tsx b/sdnr/wt/odlux/framework/src/components/titleBar.tsx index 439e9bc12..a3ba571ec 100644 --- a/sdnr/wt/odlux/framework/src/components/titleBar.tsx +++ b/sdnr/wt/odlux/framework/src/components/titleBar.tsx @@ -32,7 +32,8 @@ const styles = (theme: Theme) => createStyles({ marginRight: 20,
},
icon: {
- marginRight: 12
+ marginLeft: 16,
+ marginRight: 8
}
});
diff --git a/sdnr/wt/odlux/framework/src/design/default.ts b/sdnr/wt/odlux/framework/src/design/default.ts index ecc4ebcf2..a57685d9c 100644 --- a/sdnr/wt/odlux/framework/src/design/default.ts +++ b/sdnr/wt/odlux/framework/src/design/default.ts @@ -1,12 +1,12 @@ /****************************************************************************** * Copyright 2018 highstreet technologies GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,37 +17,28 @@ import { createMuiTheme } from '@material-ui/core/styles'; const theme = createMuiTheme({ - design: { - id: "onap", - name: "Open Networking Automation Plattform (ONAP)", - url: "https://www.onap.org/wp-content/uploads/sites/20/2017/02/logo_onap_2017.png", - height: 49, - width: 229, - logoHeight: 32, + design: { + id: "onap", + name: "Open Networking Automation Plattform (ONAP)", + url: "https://www.onap.org/wp-content/uploads/sites/20/2017/02/logo_onap_2017.png", + height: 49, + width: 229, + logoHeight: 32, + }, + palette: { + primary: { + light: "#eeeeee", + main: "#ffffff", + dark: "#e0e0e0", + contrastText: "#07819B" }, - palette: { - type: "light", - common: { - black: "#000", - white: "#fff" - }, - background: { - paper: "#fff", - default: "#fafafa" - }, - primary: { - light: "#eee", - main: "#fff", - dark: "#e0e0e0", - contrastText: "#07819B" - }, - secondary: { - light: "#07819b5e", - main: "#07819bc9", - dark: "#07819B", - contrastText: "#fff" - }, - } - }); + secondary: { + light: "rgba(7, 129, 155, 94)", + main: "rgba(7, 129, 155, 201)", + dark: "#07819B", + contrastText: "#ffffff" + }, + } +}); - export default theme;
\ No newline at end of file +export default theme;
\ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/index.html b/sdnr/wt/odlux/framework/src/index.html index 36d937775..1a373392d 100644 --- a/sdnr/wt/odlux/framework/src/index.html +++ b/sdnr/wt/odlux/framework/src/index.html @@ -1,24 +1,24 @@ -<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <!-- <link rel="stylesheet" href="./vendor.css" > -->
- <title>O D L UX</title>
-</head>
-
-<body>
- <div id="app"></div>
- <script type="text/javascript" src="./require.js"></script>
- <script type="text/javascript" src="./config.js"></script>
- <script>
- // run the application
- require(["run"], function (run) {
- run.runApplication();
- });
- </script>
-</body>
-
+<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <!-- <link rel="stylesheet" href="./vendor.css" > --> + <title>O D L UX</title> +</head> + +<body> + <div id="app"></div> + <script type="text/javascript" src="./require.js"></script> + <script type="text/javascript" src="./config.js"></script> + <script> + // run the application + require(["run"], function (run) { + run.runApplication(); + }); + </script> +</body> + </html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/models/elasticSearch.ts b/sdnr/wt/odlux/framework/src/models/elasticSearch.ts index 7c12f97d3..a9936c0ad 100644 --- a/sdnr/wt/odlux/framework/src/models/elasticSearch.ts +++ b/sdnr/wt/odlux/framework/src/models/elasticSearch.ts @@ -1,4 +1,5 @@ export type Result<TSource extends {}> = {
+ aggregations: TSource;
took: number;
timed_out: boolean;
_shards: {
@@ -30,7 +31,7 @@ type ActionResponse ={ successful: number,
failed: number
},
-
+
}
export type PostResponse = ActionResponse & {
diff --git a/sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts b/sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts index 690324e1c..bbf907c80 100644 --- a/sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts +++ b/sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts @@ -1,71 +1,71 @@ -
-import { DataCallback } from '../components/material-table';
-import { Result, HitEntry } from '../models';
-
-import { requestRest } from '../services/restService';
-
-type propType = string | number | null | undefined | (string | number)[];
-type dataType = { [prop: string]: propType };
-type resultType<TData = dataType> = { page: number, rowCount: number, rows: TData[] };
-
-export function createSearchDataHandler<TResult extends {} = dataType>(uri: string, additionalParameters?: {}): DataCallback<(TResult & { _id: string })>;
-export function createSearchDataHandler<TResult extends {} = dataType, TData = dataType>(uri: string, additionalParameters: {} | null | undefined, mapResult: (res: HitEntry<TResult>, index: number, arr: HitEntry<TResult>[]) => (TData & { _id: string }), mapRequest?: (name?: string | null) => string): DataCallback<(TData & { _id: string })>
-export function createSearchDataHandler<TResult, TData>(uri: string, additionalParameters?: {} | null | undefined, mapResult?: (res: HitEntry<TResult>, index: number, arr: HitEntry<TResult>[]) => (TData & { _id: string }), mapRequest?: (name?: string | null) => string): DataCallback<(TData & { _id: string })> {
- const url = `${ window.location.origin }/database/${uri}/_search`;
- const fetchData: DataCallback<(TData & { _id: string }) > = async (page, rowsPerPage, orderBy, order, filter) => {
- const from = rowsPerPage && page != null && !isNaN(+page)
- ? (+page) * rowsPerPage
- : null;
-
- const filterKeys = filter && Object.keys(filter) || [];
-
- const query = {
- ...filterKeys.length > 0 ? {
- query: {
- bool: {
- must: filterKeys.reduce((acc, cur) => {
- if (acc && filter && filter[cur]) {
- acc.push({ [filter[cur].indexOf("*") > -1 || filter[cur].indexOf("?") > -1 ? "wildcard" : "prefix"]: { [mapRequest ? mapRequest(cur) : cur]: filter[cur] } });
- }
- return acc;
- }, [] as any[])
- }
- }
- } : { "query": { "match_all": {} } },
- ...rowsPerPage ? { "size": rowsPerPage } : {},
- ...from ? { "from": from } : {},
- ...orderBy && order ? { "sort": [{ [mapRequest ? mapRequest(orderBy) : orderBy]: order }] } : {},
- ...additionalParameters ? additionalParameters : {}
- };
- const result = await requestRest<Result<TResult & { _id: string }>>(url, {
- method: "POST", // *GET, POST, PUT, DELETE, etc.
- mode: "no-cors", // no-cors, cors, *same-origin
- cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
- headers: {
- "Content-Type": "application/json; charset=utf-8",
- // "Content-Type": "application/x-www-form-urlencoded",
- },
- body: JSON.stringify(query), // body data type must match "Content-Type" header
- });
-
- if (result) {
- let rows: (TData & { _id: string })[] = [];
-
- if (result && result.hits && result.hits.hits) {
- rows = result.hits.hits.map( mapResult ? mapResult : h => (
- { ...(h._source as any as TData), _id: h._id }
- )) || []
- }
-
- const data = {
- page: Math.min(page || 0, result.hits.total || 0 / (rowsPerPage || 1)), rowCount: result.hits.total, rows: rows
- };
- return data;
- }
-
- return { page: 0, rowCount: 0, rows: [] };
- };
-
- return fetchData;
-}
-
+ +import { DataCallback } from '../components/material-table'; +import { Result, HitEntry } from '../models'; + +import { requestRest } from '../services/restService'; + +type propType = string | number | null | undefined | (string | number)[]; +type dataType = { [prop: string]: propType }; +type resultType<TData = dataType> = { page: number, rowCount: number, rows: TData[] }; + +export function createSearchDataHandler<TResult extends {} = dataType>(uri: string, additionalParameters?: {}): DataCallback<(TResult & { _id: string })>; +export function createSearchDataHandler<TResult extends {} = dataType, TData = dataType>(uri: string, additionalParameters: {} | null | undefined, mapResult: (res: HitEntry<TResult>, index: number, arr: HitEntry<TResult>[]) => (TData & { _id: string }), mapRequest?: (name?: string | null) => string): DataCallback<(TData & { _id: string })> +export function createSearchDataHandler<TResult, TData>(uri: string, additionalParameters?: {} | null | undefined, mapResult?: (res: HitEntry<TResult>, index: number, arr: HitEntry<TResult>[]) => (TData & { _id: string }), mapRequest?: (name?: string | null) => string): DataCallback<(TData & { _id: string })> { + const url = `${ window.location.origin }/database/${uri}/_search`; + const fetchData: DataCallback<(TData & { _id: string }) > = async (page, rowsPerPage, orderBy, order, filter) => { + const from = rowsPerPage && page != null && !isNaN(+page) + ? (+page) * rowsPerPage + : null; + + const filterKeys = filter && Object.keys(filter) || []; + + const query = { + ...filterKeys.length > 0 ? { + query: { + bool: { + must: filterKeys.reduce((acc, cur) => { + if (acc && filter && filter[cur]) { + acc.push({ [filter[cur].indexOf("*") > -1 || filter[cur].indexOf("?") > -1 ? "wildcard" : "term"]: { [mapRequest ? mapRequest(cur) : cur]: filter[cur] } }); + } + return acc; + }, [] as any[]) + } + } + } : { "query": { "match_all": {} } }, + ...rowsPerPage ? { "size": rowsPerPage } : {}, + ...from ? { "from": from } : {}, + ...orderBy && order ? { "sort": [{ [mapRequest ? mapRequest(orderBy) : orderBy]: order }] } : {}, + ...additionalParameters ? additionalParameters : {} + }; + const result = await requestRest<Result<TResult & { _id: string }>>(url, { + method: "POST", // *GET, POST, PUT, DELETE, etc. + mode: "no-cors", // no-cors, cors, *same-origin + cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached + headers: { + "Content-Type": "application/json; charset=utf-8", + // "Content-Type": "application/x-www-form-urlencoded", + }, + body: JSON.stringify(query), // body data type must match "Content-Type" header + }); + + if (result) { + let rows: (TData & { _id: string })[] = []; + + if (result && result.hits && result.hits.hits) { + rows = result.hits.hits.map( mapResult ? mapResult : h => ( + { ...(h._source as any as TData), _id: h._id } + )) || [] + } + + const data = { + page: Math.min(page || 0, result.hits.total || 0 / (rowsPerPage || 1)), rowCount: result.hits.total, rows: rows + }; + return data; + } + + return { page: 0, rowCount: 0, rows: [] }; + }; + + return fetchData; +} + diff --git a/sdnr/wt/odlux/framework/src/views/about.tsx b/sdnr/wt/odlux/framework/src/views/about.tsx index f905e0e75..4cd544b5f 100644 --- a/sdnr/wt/odlux/framework/src/views/about.tsx +++ b/sdnr/wt/odlux/framework/src/views/about.tsx @@ -1,859 +1,14 @@ import * as React from 'react';
-import { withComponents, WithComponents } from '../utilities/withComponents';
-import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles';
-
-import ExpansionPanel from '@material-ui/core/ExpansionPanel';
-import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
-import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
-import Typography from '@material-ui/core/Typography';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
-
-import { MaterialTable, MaterialTableCtorType, ColumnType } from '../components/material-table';
-import { TreeView, ITreeItem, TreeViewCtorType } from '../components/material-ui/treeView';
-import { SvgIconProps } from '@material-ui/core/SvgIcon';
-
-const styles = (theme: Theme) => createStyles({
- root: {
- width: '100%',
- },
- heading: {
- fontSize: theme.typography.pxToRem(15),
- fontWeight: theme.typography.fontWeightRegular,
- },
-});
-
-class SampleData {
- _id: string;
- index: number;
- guid: string;
- isActive: boolean;
- balance: string;
- age: number;
- firstName: string;
- lastName: string;
- company: string;
- email: string;
- registered: string;
- latitude: string;
- longitude: string;
-}
-
-// https://next.json-generator.com/NJ5Bv-v1I
-const tableData: SampleData[] = [
- {
- "_id": "5c0e18399919a5c43636fdf2",
- "index": 0,
- "guid": "48728d8e-8300-4d0f-b967-e2166d023066",
- "isActive": false,
- "balance": "$3,480.16",
- "age": 33,
- "firstName": "Brooke",
- "lastName": "Morris",
- "company": "ZORROMOP",
- "email": "brooke.morris@zorromop.de",
- "registered": "Sunday, February 11, 2018 2:55 PM",
- "latitude": "-69.109379",
- "longitude": "113.735639"
- },
- {
- "_id": "5c0e1839b61e3eeaf164259d",
- "index": 1,
- "guid": "28723570-1507-422e-b78c-924402371fb1",
- "isActive": false,
- "balance": "$1,305.01",
- "age": 28,
- "firstName": "Jolene",
- "lastName": "Everett",
- "company": "ZENCO",
- "email": "jolene.everett@zenco.de",
- "registered": "Saturday, December 8, 2018 5:17 PM",
- "latitude": "13.683025",
- "longitude": "85.101421"
- },
- {
- "_id": "5c0e1839e81f57913c5d2147",
- "index": 2,
- "guid": "e914dc5d-91a3-405d-ac48-aee6f0cd391a",
- "isActive": true,
- "balance": "$1,418.37",
- "age": 28,
- "firstName": "Elva",
- "lastName": "Travis",
- "company": "ZYTREK",
- "email": "elva.travis@zytrek.de",
- "registered": "Thursday, March 10, 2016 5:13 PM",
- "latitude": "53.75862",
- "longitude": "-67.784532"
- },
- {
- "_id": "5c0e1839bc9224a2b54c0f69",
- "index": 3,
- "guid": "88cbdce0-0bcc-4d16-83c3-3017690503c4",
- "isActive": true,
- "balance": "$1,709.60",
- "age": 21,
- "firstName": "Ellis",
- "lastName": "Mcpherson",
- "company": "DIGIPRINT",
- "email": "ellis.mcpherson@digiprint.de",
- "registered": "Sunday, December 21, 2014 5:25 AM",
- "latitude": "46.486149",
- "longitude": "-66.657067"
- },
- {
- "_id": "5c0e183951b51475db0f35d1",
- "index": 4,
- "guid": "c887ac86-7ba1-4eb6-9b47-e88a1bcb3713",
- "isActive": true,
- "balance": "$3,578.54",
- "age": 25,
- "firstName": "Marcia",
- "lastName": "Rocha",
- "company": "ZAPPIX",
- "email": "marcia.rocha@zappix.de",
- "registered": "Tuesday, June 16, 2015 11:21 AM",
- "latitude": "-39.905461",
- "longitude": "150.873895"
- },
- {
- "_id": "5c0e18398c5be8d362a578eb",
- "index": 5,
- "guid": "0d160697-9b5b-4941-9b5f-4ba3a7f97b49",
- "isActive": true,
- "balance": "$414.98",
- "age": 32,
- "firstName": "Lavonne",
- "lastName": "Wilkins",
- "company": "FARMAGE",
- "email": "lavonne.wilkins@farmage.de",
- "registered": "Monday, February 1, 2016 5:27 PM",
- "latitude": "-16.839256",
- "longitude": "-105.824746"
- },
- {
- "_id": "5c0e18399804086c836d7d56",
- "index": 6,
- "guid": "715a5f63-35b6-4903-a46e-ba584b005e64",
- "isActive": false,
- "balance": "$1,755.78",
- "age": 32,
- "firstName": "Wise",
- "lastName": "Berg",
- "company": "ZIZZLE",
- "email": "wise.berg@zizzle.de",
- "registered": "Saturday, March 28, 2015 1:40 AM",
- "latitude": "51.15269",
- "longitude": "65.795093"
- },
- {
- "_id": "5c0e18399c4d13538bcaf8c9",
- "index": 7,
- "guid": "7ee50269-23e8-499e-9a16-09f393d7600c",
- "isActive": false,
- "balance": "$342.52",
- "age": 27,
- "firstName": "Isabel",
- "lastName": "Battle",
- "company": "EZENTIA",
- "email": "isabel.battle@ezentia.de",
- "registered": "Thursday, June 7, 2018 12:16 AM",
- "latitude": "-53.318152",
- "longitude": "-153.516824"
- },
- {
- "_id": "5c0e18398d7fb9a4eceeffa2",
- "index": 8,
- "guid": "1e30c9ac-2297-4f16-83e6-9559b1ebe92c",
- "isActive": true,
- "balance": "$3,184.71",
- "age": 36,
- "firstName": "Lenora",
- "lastName": "Crawford",
- "company": "KIDGREASE",
- "email": "lenora.crawford@kidgrease.de",
- "registered": "Saturday, January 7, 2017 6:17 PM",
- "latitude": "-72.431496",
- "longitude": "9.413359"
- },
- {
- "_id": "5c0e18395837069ab6b79d00",
- "index": 9,
- "guid": "d04a02ed-5899-4729-a7e5-2d85b5d03973",
- "isActive": true,
- "balance": "$1,553.28",
- "age": 35,
- "firstName": "Sasha",
- "lastName": "Bridges",
- "company": "IDEALIS",
- "email": "sasha.bridges@idealis.de",
- "registered": "Sunday, February 4, 2018 7:02 PM",
- "latitude": "8.095691",
- "longitude": "-105.758195"
- },
- {
- "_id": "5c0e18390be19bf65acad180",
- "index": 10,
- "guid": "3a1a77e6-ef15-4598-8274-c68ac3bb922a",
- "isActive": false,
- "balance": "$3,587.96",
- "age": 20,
- "firstName": "Wilkins",
- "lastName": "Beasley",
- "company": "DIGIFAD",
- "email": "wilkins.beasley@digifad.de",
- "registered": "Monday, March 5, 2018 1:27 PM",
- "latitude": "-88.062704",
- "longitude": "149.95661"
- },
- {
- "_id": "5c0e1839ffbbad5c9954e49f",
- "index": 11,
- "guid": "97a56950-a08c-4e00-8002-ba2d5de4da5d",
- "isActive": false,
- "balance": "$1,997.80",
- "age": 31,
- "firstName": "Sullivan",
- "lastName": "Mcclain",
- "company": "EARTHMARK",
- "email": "sullivan.mcclain@earthmark.de",
- "registered": "Saturday, October 27, 2018 2:51 PM",
- "latitude": "-81.86349",
- "longitude": "-79.596991"
- },
- {
- "_id": "5c0e183914bd464d55e7325f",
- "index": 12,
- "guid": "294f6485-d0f9-4b25-b998-325ae90fa769",
- "isActive": true,
- "balance": "$1,405.46",
- "age": 24,
- "firstName": "Herminia",
- "lastName": "Fischer",
- "company": "ECOLIGHT",
- "email": "herminia.fischer@ecolight.de",
- "registered": "Thursday, January 16, 2014 4:48 PM",
- "latitude": "48.224363",
- "longitude": "11.08339"
- },
- {
- "_id": "5c0e183968ec2556d8f6566c",
- "index": 13,
- "guid": "16edfea4-7b37-4e54-868c-c369b413dd78",
- "isActive": false,
- "balance": "$3,440.67",
- "age": 39,
- "firstName": "Blanchard",
- "lastName": "Blackwell",
- "company": "GEOFORMA",
- "email": "blanchard.blackwell@geoforma.de",
- "registered": "Wednesday, July 30, 2014 4:07 AM",
- "latitude": "-52.169297",
- "longitude": "10.415879"
- },
- {
- "_id": "5c0e183939a0fc955f2d94da",
- "index": 14,
- "guid": "4ed454e2-dde1-4ab5-a434-4a82205ced2d",
- "isActive": true,
- "balance": "$1,883.27",
- "age": 35,
- "firstName": "Gayle",
- "lastName": "Little",
- "company": "AQUAZURE",
- "email": "gayle.little@aquazure.de",
- "registered": "Tuesday, December 12, 2017 5:08 PM",
- "latitude": "-58.473236",
- "longitude": "38.022269"
- },
- {
- "_id": "5c0e1839099f9221ccd968ac",
- "index": 15,
- "guid": "1d052fd4-7c54-45fb-b0db-7de1acc4262a",
- "isActive": false,
- "balance": "$2,601.94",
- "age": 31,
- "firstName": "Jocelyn",
- "lastName": "Richards",
- "company": "GINK",
- "email": "jocelyn.richards@gink.de",
- "registered": "Sunday, October 30, 2016 9:12 PM",
- "latitude": "-43.489676",
- "longitude": "2.557869"
- },
- {
- "_id": "5c0e183970f320f377321c3f",
- "index": 16,
- "guid": "45bca125-8831-48c3-b22b-29ae318e7096",
- "isActive": false,
- "balance": "$3,441.74",
- "age": 34,
- "firstName": "Berta",
- "lastName": "Valentine",
- "company": "ISOSPHERE",
- "email": "berta.valentine@isosphere.de",
- "registered": "Sunday, March 19, 2017 8:22 PM",
- "latitude": "-40.188039",
- "longitude": "-170.085092"
- },
- {
- "_id": "5c0e1839ab960bb0a9f4f392",
- "index": 17,
- "guid": "d7b5122a-94c9-423c-b799-1a8f8314b152",
- "isActive": false,
- "balance": "$56.39",
- "age": 21,
- "firstName": "Russell",
- "lastName": "Powers",
- "company": "TETAK",
- "email": "russell.powers@tetak.de",
- "registered": "Thursday, November 3, 2016 9:23 PM",
- "latitude": "-51.610519",
- "longitude": "-133.280363"
- },
- {
- "_id": "5c0e183998f0195404b9aaa4",
- "index": 18,
- "guid": "a043ba97-ea7e-48ce-bb15-18ee09fb393d",
- "isActive": true,
- "balance": "$1,503.57",
- "age": 37,
- "firstName": "Rosario",
- "lastName": "Brennan",
- "company": "VIAGRAND",
- "email": "rosario.brennan@viagrand.de",
- "registered": "Saturday, March 17, 2018 10:32 PM",
- "latitude": "-43.773365",
- "longitude": "47.58682"
- },
- {
- "_id": "5c0e1839bcb2a5cc567129ac",
- "index": 19,
- "guid": "de6d5d36-201e-4f87-9976-ed31f3160e42",
- "isActive": false,
- "balance": "$1,160.18",
- "age": 29,
- "firstName": "Anita",
- "lastName": "Hodges",
- "company": "TUBALUM",
- "email": "anita.hodges@tubalum.de",
- "registered": "Sunday, November 26, 2017 11:54 AM",
- "latitude": "7.080244",
- "longitude": "-9.970715"
- },
- {
- "_id": "5c0e18394b37e854a1ef371c",
- "index": 20,
- "guid": "9407113b-896a-4699-ac1b-363bc3c6f8ad",
- "isActive": false,
- "balance": "$34.81",
- "age": 31,
- "firstName": "Barrett",
- "lastName": "Weaver",
- "company": "DUOFLEX",
- "email": "barrett.weaver@duoflex.de",
- "registered": "Tuesday, November 3, 2015 9:31 AM",
- "latitude": "40.30558",
- "longitude": "-69.986664"
- },
- {
- "_id": "5c0e1839b5658f90e16a86e0",
- "index": 21,
- "guid": "81f894c4-c931-422d-a30e-593824d95bf9",
- "isActive": true,
- "balance": "$2,808.63",
- "age": 26,
- "firstName": "Baxter",
- "lastName": "Chase",
- "company": "BUNGA",
- "email": "baxter.chase@bunga.de",
- "registered": "Friday, October 28, 2016 7:10 AM",
- "latitude": "-49.05652",
- "longitude": "63.123535"
- },
- {
- "_id": "5c0e1839cb9462c9ecbb59af",
- "index": 22,
- "guid": "92e67862-4fdf-43af-a3ef-ef3edb8d6706",
- "isActive": true,
- "balance": "$3,552.71",
- "age": 29,
- "firstName": "Olga",
- "lastName": "Kemp",
- "company": "OHMNET",
- "email": "olga.kemp@ohmnet.de",
- "registered": "Saturday, March 26, 2016 11:51 AM",
- "latitude": "-17.450481",
- "longitude": "-13.945794"
- },
- {
- "_id": "5c0e18396f999c2b8ac731a9",
- "index": 23,
- "guid": "a682eaae-34f0-4973-b8a0-30972de0732b",
- "isActive": false,
- "balance": "$1,999.20",
- "age": 21,
- "firstName": "Ebony",
- "lastName": "Le",
- "company": "MULTRON",
- "email": "ebony.le@multron.de",
- "registered": "Friday, March 27, 2015 9:23 AM",
- "latitude": "-70.380014",
- "longitude": "173.20685"
- },
- {
- "_id": "5c0e18391cfb28263eb42db7",
- "index": 24,
- "guid": "f1cddb5f-0b89-453e-b0c9-8193a56cc610",
- "isActive": true,
- "balance": "$2,950.91",
- "age": 30,
- "firstName": "Norman",
- "lastName": "Price",
- "company": "COMVEX",
- "email": "norman.price@comvex.de",
- "registered": "Tuesday, August 21, 2018 11:17 PM",
- "latitude": "86.501469",
- "longitude": "159.545352"
- },
- {
- "_id": "5c0e18394a6be11128c7e5ca",
- "index": 25,
- "guid": "dadb738a-40fd-45b6-abac-023a803d95c2",
- "isActive": true,
- "balance": "$2,767.09",
- "age": 25,
- "firstName": "Sara",
- "lastName": "Ruiz",
- "company": "AUSTECH",
- "email": "sara.ruiz@austech.de",
- "registered": "Wednesday, June 20, 2018 6:34 AM",
- "latitude": "86.784904",
- "longitude": "-120.331325"
- },
- {
- "_id": "5c0e183974631549eda97cea",
- "index": 26,
- "guid": "b5c43ee5-14ed-4ab5-b3db-b31a8bb65ceb",
- "isActive": true,
- "balance": "$3,235.42",
- "age": 32,
- "firstName": "Holly",
- "lastName": "Santos",
- "company": "LOVEPAD",
- "email": "holly.santos@lovepad.de",
- "registered": "Thursday, November 22, 2018 9:26 PM",
- "latitude": "-19.640066",
- "longitude": "50.410992"
- },
- {
- "_id": "5c0e1839ab9b933881429d78",
- "index": 27,
- "guid": "94961092-65ca-41b9-bc69-3e40ce2cafc9",
- "isActive": true,
- "balance": "$2,106.34",
- "age": 39,
- "firstName": "Rachel",
- "lastName": "Douglas",
- "company": "DEMINIMUM",
- "email": "rachel.douglas@deminimum.de",
- "registered": "Sunday, April 9, 2017 3:55 AM",
- "latitude": "31.395281",
- "longitude": "-1.899514"
- },
- {
- "_id": "5c0e183937f743155859c5a9",
- "index": 28,
- "guid": "07d7ef18-bcef-483d-999e-0b3da4a7098b",
- "isActive": true,
- "balance": "$2,260.65",
- "age": 40,
- "firstName": "Reed",
- "lastName": "Workman",
- "company": "BUZZMAKER",
- "email": "reed.workman@buzzmaker.de",
- "registered": "Wednesday, May 28, 2014 3:44 PM",
- "latitude": "23.789646",
- "longitude": "106.938375"
- },
- {
- "_id": "5c0e1839f8f4b60beb28b7ed",
- "index": 29,
- "guid": "9b4952e5-aa0e-4919-9e17-7c357a297394",
- "isActive": false,
- "balance": "$702.99",
- "age": 27,
- "firstName": "Cochran",
- "lastName": "Ware",
- "company": "HIVEDOM",
- "email": "cochran.ware@hivedom.de",
- "registered": "Monday, October 16, 2017 5:51 AM",
- "latitude": "85.953108",
- "longitude": "124.590037"
- },
- {
- "_id": "5c0e1839342fbd54a88269df",
- "index": 30,
- "guid": "30937d5b-9514-4ebd-b628-2cfb5017fe41",
- "isActive": false,
- "balance": "$385.88",
- "age": 35,
- "firstName": "Cote",
- "lastName": "Hess",
- "company": "TERAPRENE",
- "email": "cote.hess@teraprene.de",
- "registered": "Thursday, March 15, 2018 4:42 PM",
- "latitude": "81.38211",
- "longitude": "64.516797"
- },
- {
- "_id": "5c0e18395b6dc85d73ce1fb3",
- "index": 31,
- "guid": "f34847da-7f96-4cd8-8d8a-b06c0eb0a8f2",
- "isActive": true,
- "balance": "$3,494.56",
- "age": 27,
- "firstName": "Daniels",
- "lastName": "Ayala",
- "company": "BESTO",
- "email": "daniels.ayala@besto.de",
- "registered": "Sunday, December 18, 2016 10:52 AM",
- "latitude": "47.704227",
- "longitude": "41.674767"
- },
- {
- "_id": "5c0e183974587cdccf30b13f",
- "index": 32,
- "guid": "fdbb6d83-0e47-4453-b8a7-b47f44e4164b",
- "isActive": false,
- "balance": "$2,087.38",
- "age": 26,
- "firstName": "Powers",
- "lastName": "Drake",
- "company": "GENESYNK",
- "email": "powers.drake@genesynk.de",
- "registered": "Saturday, September 29, 2018 12:24 AM",
- "latitude": "40.580432",
- "longitude": "110.940759"
- },
- {
- "_id": "5c0e18397b51245e971c58b8",
- "index": 33,
- "guid": "6adfe544-238b-4001-b2a6-f50ea3094da3",
- "isActive": true,
- "balance": "$3,566.22",
- "age": 34,
- "firstName": "Pacheco",
- "lastName": "Ramsey",
- "company": "ENVIRE",
- "email": "pacheco.ramsey@envire.de",
- "registered": "Friday, September 11, 2015 12:14 AM",
- "latitude": "-30.691235",
- "longitude": "69.343692"
- },
- {
- "_id": "5c0e18391ede9c0996fd09e7",
- "index": 34,
- "guid": "d190b32f-d33b-4c17-a18a-bb2f57e79ba7",
- "isActive": false,
- "balance": "$1,671.63",
- "age": 32,
- "firstName": "Mcintyre",
- "lastName": "Chan",
- "company": "ORBAXTER",
- "email": "mcintyre.chan@orbaxter.de",
- "registered": "Wednesday, May 7, 2014 7:11 PM",
- "latitude": "7.380435",
- "longitude": "70.955103"
- },
- {
- "_id": "5c0e1839fe48069c9c260fa9",
- "index": 35,
- "guid": "a41c064b-6bf4-4ba5-b229-9b657d286936",
- "isActive": false,
- "balance": "$24.02",
- "age": 27,
- "firstName": "Genevieve",
- "lastName": "Sparks",
- "company": "ZBOO",
- "email": "genevieve.sparks@zboo.de",
- "registered": "Saturday, December 16, 2017 2:51 PM",
- "latitude": "-63.406337",
- "longitude": "118.662621"
- },
- {
- "_id": "5c0e1839a7e8e76accf0803e",
- "index": 36,
- "guid": "3e71864d-4be5-418e-ace8-346c3d7a9c5f",
- "isActive": true,
- "balance": "$3,261.01",
- "age": 30,
- "firstName": "Powell",
- "lastName": "Patterson",
- "company": "GAZAK",
- "email": "powell.patterson@gazak.de",
- "registered": "Thursday, May 18, 2017 10:10 AM",
- "latitude": "-10.428548",
- "longitude": "64.979192"
- },
- {
- "_id": "5c0e183984b0320f1118a8b0",
- "index": 37,
- "guid": "ec5b292c-6efb-471b-9bf5-a47286e03515",
- "isActive": false,
- "balance": "$918.71",
- "age": 37,
- "firstName": "Tara",
- "lastName": "Mcmillan",
- "company": "GRAINSPOT",
- "email": "tara.mcmillan@grainspot.de",
- "registered": "Sunday, May 17, 2015 1:01 PM",
- "latitude": "-13.519031",
- "longitude": "67.931062"
- },
- {
- "_id": "5c0e183965875876835ccd79",
- "index": 38,
- "guid": "b7e97ffb-439a-4454-90af-7f5ebd565ebc",
- "isActive": true,
- "balance": "$574.99",
- "age": 28,
- "firstName": "Pennington",
- "lastName": "Gallegos",
- "company": "CEDWARD",
- "email": "pennington.gallegos@cedward.de",
- "registered": "Wednesday, September 26, 2018 6:01 AM",
- "latitude": "-63.693261",
- "longitude": "-38.352153"
- },
- {
- "_id": "5c0e183922505dd21be49009",
- "index": 39,
- "guid": "5187aa39-4357-462b-9508-3c537d26d70d",
- "isActive": false,
- "balance": "$2,447.08",
- "age": 26,
- "firstName": "Meagan",
- "lastName": "Irwin",
- "company": "SENTIA",
- "email": "meagan.irwin@sentia.de",
- "registered": "Saturday, April 2, 2016 4:39 PM",
- "latitude": "1.051313",
- "longitude": "-86.168315"
- },
- {
- "_id": "5c0e183900a9f7f896e5b3b1",
- "index": 40,
- "guid": "31889843-79e7-4636-9ca1-4eb5cbcb0ae3",
- "isActive": true,
- "balance": "$1,992.25",
- "age": 22,
- "firstName": "Kelly",
- "lastName": "Cobb",
- "company": "BOVIS",
- "email": "kelly.cobb@bovis.de",
- "registered": "Tuesday, August 9, 2016 5:36 PM",
- "latitude": "-85.547579",
- "longitude": "-89.794104"
- },
- {
- "_id": "5c0e18393b25b8552ff950e2",
- "index": 41,
- "guid": "0bf02edc-ca1b-4cfe-8356-b65881bdca11",
- "isActive": true,
- "balance": "$465.96",
- "age": 27,
- "firstName": "Angela",
- "lastName": "Booker",
- "company": "EQUICOM",
- "email": "angela.booker@equicom.de",
- "registered": "Thursday, July 30, 2015 1:39 AM",
- "latitude": "-9.345395",
- "longitude": "107.070665"
- },
- {
- "_id": "5c0e183955d747ebbe25437b",
- "index": 42,
- "guid": "6405e559-5849-4d12-ae4e-520f13b4dffe",
- "isActive": true,
- "balance": "$15.63",
- "age": 28,
- "firstName": "Carrie",
- "lastName": "Mclean",
- "company": "BOINK",
- "email": "carrie.mclean@boink.de",
- "registered": "Wednesday, February 1, 2017 1:50 PM",
- "latitude": "72.287519",
- "longitude": "-135.436286"
- },
- {
- "_id": "5c0e1839e9cfe1b28e31e7e6",
- "index": 43,
- "guid": "e49e7ca7-a6cc-4cdb-bebe-5a3b6ba931eb",
- "isActive": true,
- "balance": "$3,127.94",
- "age": 33,
- "firstName": "Callie",
- "lastName": "Cooley",
- "company": "MUSIX",
- "email": "callie.cooley@musix.de",
- "registered": "Wednesday, August 30, 2017 4:58 PM",
- "latitude": "-38.954739",
- "longitude": "-152.706424"
- },
- {
- "_id": "5c0e18391bafa0750ff4f280",
- "index": 44,
- "guid": "c245ffd3-4924-4dce-ae4a-f4cabf057b54",
- "isActive": false,
- "balance": "$1,320.36",
- "age": 35,
- "firstName": "Terry",
- "lastName": "Bennett",
- "company": "EXOTECHNO",
- "email": "terry.bennett@exotechno.de",
- "registered": "Friday, June 17, 2016 11:54 PM",
- "latitude": "-48.946183",
- "longitude": "32.53167"
- },
- {
- "_id": "5c0e1839e91b27fcce34b70f",
- "index": 45,
- "guid": "0860cb66-de4c-410e-8233-aeef5ee9d64e",
- "isActive": false,
- "balance": "$1,187.75",
- "age": 30,
- "firstName": "Phoebe",
- "lastName": "Bartlett",
- "company": "VORATAK",
- "email": "phoebe.bartlett@voratak.de",
- "registered": "Tuesday, July 25, 2017 2:57 AM",
- "latitude": "-63.208957",
- "longitude": "-91.209743"
- },
- {
- "_id": "5c0e183987e8a4e98415c8dd",
- "index": 46,
- "guid": "49219833-172c-4659-9192-d1116a5ca833",
- "isActive": false,
- "balance": "$3,225.24",
- "age": 38,
- "firstName": "Jordan",
- "lastName": "Evans",
- "company": "PHARMACON",
- "email": "jordan.evans@pharmacon.de",
- "registered": "Sunday, April 23, 2017 6:27 PM",
- "latitude": "-59.454678",
- "longitude": "67.251185"
- },
- {
- "_id": "5c0e183944979692cc1a3e48",
- "index": 47,
- "guid": "680c4d15-d539-4db9-8793-a2f6d3f354aa",
- "isActive": false,
- "balance": "$2,913.14",
- "age": 28,
- "firstName": "Goodman",
- "lastName": "Cain",
- "company": "CAXT",
- "email": "goodman.cain@caxt.de",
- "registered": "Tuesday, November 1, 2016 6:11 PM",
- "latitude": "-30.187547",
- "longitude": "-164.313273"
- },
- {
- "_id": "5c0e1839ef5312ac08e3cbc3",
- "index": 48,
- "guid": "85f5fa5d-b6b3-47c6-ad1b-faee10a4e1bd",
- "isActive": true,
- "balance": "$544.97",
- "age": 27,
- "firstName": "Aisha",
- "lastName": "Oliver",
- "company": "MINGA",
- "email": "aisha.oliver@minga.de",
- "registered": "Sunday, July 3, 2016 8:18 AM",
- "latitude": "-21.527536",
- "longitude": "141.029691"
- },
- {
- "_id": "5c0e1839c2e58f5da04f29fd",
- "index": 49,
- "guid": "e2ee9b25-5887-49a9-a1c6-17432154d266",
- "isActive": true,
- "balance": "$3,621.65",
- "age": 31,
- "firstName": "Erin",
- "lastName": "Lester",
- "company": "SLOFAST",
- "email": "erin.lester@slofast.de",
- "registered": "Saturday, February 20, 2016 5:13 AM",
- "latitude": "-30.080798",
- "longitude": "-1.291093"
- }
-];
-
-const components = {
- 'counter': 'demoApp.counter'
-};
-
-class TreeDemoItem implements ITreeItem {
- title: string;
- children?: TreeDemoItem[];
- disabled?: boolean;
- icon?: React.ComponentType<SvgIconProps>;
-}
-
-const treeData: TreeDemoItem[] = [
- { title: "Erste Ebene", children: [
- { title: "Zweite Ebene", children: [
- { title: "Dritte Ebene" },
- ]
- },
- { title: "Zweite Ebene 2" },
- ]
- },
- { title: "Erste Ebene 3" },
-];
-
-const SampleDataMaterialTable = MaterialTable as MaterialTableCtorType<SampleData>;
-
-const SampleTree = TreeView as any as TreeViewCtorType<TreeDemoItem>;
-
-const AboutComponent = (props: WithComponents<typeof components> & WithStyles<typeof styles>) => {
+const AboutComponent = () => {
return (
<div>
<h2>About</h2>
- <ExpansionPanel>
- <ExpansionPanelSummary expandIcon={ <ExpandMoreIcon /> }>
- <Typography className={ props.classes.heading }>Client Side Table Demo</Typography>
- </ExpansionPanelSummary>
- <ExpansionPanelDetails>
- <SampleDataMaterialTable rows={ tableData } columns={
- [
- { property: "index", type: ColumnType.text, title: "Index", width: "80px", disableFilter:true, disableSorting:true, disablePadding:true },
- { property: "firstName", type: ColumnType.text, title: "First Name" },
- { property: "lastName", type: ColumnType.text, title: "Last Name" },
- { property: "age", type: ColumnType.numeric, title: "Age", width: "60px" },
- { property: "email", type: ColumnType.text, title: "eMail" },
- { property: "actions", type: ColumnType.custom, title: "Actions", customControl: ({ rowData }) => (<div>Button</div>) },
- ]
- } idProperty={ "_id" } title={ "Customers 2018" } >
- </SampleDataMaterialTable>
- </ExpansionPanelDetails>
- </ExpansionPanel>
- <ExpansionPanel>
- <ExpansionPanelSummary expandIcon={ <ExpandMoreIcon /> }>
- <Typography className={ props.classes.heading }>Tree Demo</Typography>
- </ExpansionPanelSummary>
- <ExpansionPanelDetails>
- <SampleTree items={ treeData } contentProperty={"title"} childrenProperty={"children"} useFolderIcons enableSearchBar />
- </ExpansionPanelDetails>
- </ExpansionPanel>
+ <div style={{ margin: "0 auto" }}>##odlux.version##</div>
</div>
- )
+ );
};
-export const About = withComponents(components)(withStyles(styles)(AboutComponent));
+export const About = AboutComponent;
export default About;
\ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/views/frame.tsx b/sdnr/wt/odlux/framework/src/views/frame.tsx index fd943319d..69b981bb3 100644 --- a/sdnr/wt/odlux/framework/src/views/frame.tsx +++ b/sdnr/wt/odlux/framework/src/views/frame.tsx @@ -13,6 +13,7 @@ import SnackDisplay from '../components/material-ui/snackDisplay'; import Home from '../views/home';
import Login from '../views/login';
import About from '../views/about';
+import Test from '../views/test';
import applicationService from '../services/applicationManager';
import { SnackbarProvider } from 'notistack';
@@ -28,7 +29,7 @@ const styles = (theme: Theme) => createStyles({ },
content: {
flexGrow: 1,
- display: "flex",
+ display: "flex",
flexDirection: "column",
backgroundColor: theme.palette.background.default,
padding: theme.spacing.unit * 3,
@@ -59,7 +60,12 @@ export const Frame = withStyles(styles)(({ classes }: WithStyles<typeof styles>) <AppFrame title={ "About" } icon={ faAddressBook } >
<About />
</AppFrame>
- ) } />
+ )} />
+ { process.env.NODE_ENV === "development" ? <Route path="/test" component={() => (
+ <AppFrame title={"Test"} icon={faAddressBook} >
+ <Test />
+ </AppFrame>
+ )} /> : null}
<Route path="/login" component={ () => (
<AppFrame title={ "Login" } icon={ faSignInAlt } >
<Login />
@@ -82,4 +88,4 @@ export const Frame = withStyles(styles)(({ classes }: WithStyles<typeof styles>) );
});
-export default Frame;
+export default Frame;
diff --git a/sdnr/wt/odlux/framework/src/views/home.tsx b/sdnr/wt/odlux/framework/src/views/home.tsx index 3d7497401..18f94f70b 100644 --- a/sdnr/wt/odlux/framework/src/views/home.tsx +++ b/sdnr/wt/odlux/framework/src/views/home.tsx @@ -1,36 +1,9 @@ import * as React from 'react';
-import Button from '@material-ui/core/Button';
-
-class BuggyCounter extends React.Component<{}, {counter:number}> {
- constructor(props: {}) {
- super(props);
- this.state = { counter: 0 };
- this.handleClick = this.handleClick.bind(this);
- }
-
- handleClick() {
- this.setState(({ counter }) => ({
- counter: counter + 1
- }));
- }
-
- render() {
- if (this.state.counter === 5) {
- // Simulate a JS error
- throw new Error('I crashed!');
- }
- return <h1 onClick={ this.handleClick }>{ this.state.counter }</h1>;
- }
-}
export const Home = (props: React.Props<any>) => {
return (
<div>
<h1>Welcome to ODLUX.</h1>
- <Button variant="contained" color="secondary" onClick={ () => { throw new Error("This is an error") } }>
- Throw an Error1
- </Button>
- <BuggyCounter />
</div>
)
}
diff --git a/sdnr/wt/odlux/framework/src/views/login.tsx b/sdnr/wt/odlux/framework/src/views/login.tsx index 513e24712..054ae4f2e 100644 --- a/sdnr/wt/odlux/framework/src/views/login.tsx +++ b/sdnr/wt/odlux/framework/src/views/login.tsx @@ -41,7 +41,7 @@ const styles = (theme: Theme) => createStyles({ display: 'flex', flexDirection: 'column', alignItems: 'center', - padding: `${ theme.spacing.unit * 2 }px ${ theme.spacing.unit * 3 }px ${ theme.spacing.unit * 3 }px`, + padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`, }, avatar: { margin: theme.spacing.unit, @@ -56,11 +56,11 @@ const styles = (theme: Theme) => createStyles({ }, }); -type LoginProps = RouteComponentProps<{}> & WithStyles<typeof styles> & Connect ; +type LoginProps = RouteComponentProps<{}> & WithStyles<typeof styles> & Connect; interface ILoginState { busy: boolean; - email: string; + username: string; password: string; scope: string; message: string; @@ -70,12 +70,12 @@ interface ILoginState { // todo: ggf. redirect to einbauen class LoginComponent extends React.Component<LoginProps, ILoginState> { - constructor(props: LoginProps) { + constructor (props: LoginProps) { super(props); this.state = { busy: false, - email: '', + username: '', password: '', scope: 'sdn', message: '' @@ -87,19 +87,19 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { return ( <React.Fragment> <CssBaseline /> - <main className={ classes.layout }> - <Paper className={ classes.paper }> - <Avatar className={ classes.avatar }> + <main className={classes.layout}> + <Paper className={classes.paper}> + <Avatar className={classes.avatar}> <LockIcon /> </Avatar> <Typography variant="caption">Sign in</Typography> - <form className={ classes.form }> + <form className={classes.form}> <FormControl margin="normal" required fullWidth> - <InputLabel htmlFor="email">Email Address</InputLabel> - <Input id="email" name="email" autoComplete="email" autoFocus - disabled={ this.state.busy } - value = {this.state.email } - onChange={ event => { this.setState({ email: event.target.value }) } }/> + <InputLabel htmlFor="username">Username</InputLabel> + <Input id="username" name="username" autoComplete="username" autoFocus + disabled={this.state.busy} + value={this.state.username} + onChange={event => { this.setState({ username: event.target.value }) }} /> </FormControl> <FormControl margin="normal" required fullWidth> <InputLabel htmlFor="password">Password</InputLabel> @@ -108,13 +108,13 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { type="password" id="password" autoComplete="current-password" - disabled={ this.state.busy } - value={ this.state.password } - onChange={ event => { this.setState({ password: event.target.value }) } } + disabled={this.state.busy} + value={this.state.password} + onChange={event => { this.setState({ password: event.target.value }) }} /> </FormControl> <FormControl margin="normal" required fullWidth> - <InputLabel htmlFor="password">Scope</InputLabel> + <InputLabel htmlFor="password">Domain</InputLabel> <Input name="scope" type="scope" @@ -125,7 +125,7 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { /> </FormControl> <FormControlLabel - control={ <Checkbox value="remember" color="primary" /> } + control={<Checkbox value="remember" color="primary" />} label="Remember me" /> <Button @@ -133,9 +133,9 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { fullWidth variant="raised" color="primary" - disabled = { this.state.busy } - className={ classes.submit } - onClick = { this.onSignIn } + disabled={this.state.busy} + className={classes.submit} + onClick={this.onSignIn} > Sign in </Button> @@ -144,14 +144,14 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { </main> <Dialog open={!!this.state.message} - onClose={() => { this.setState({message: ''})}} + onClose={() => { this.setState({ message: '' }) }} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > <DialogTitle id="alert-dialog-title">{"Error"}</DialogTitle> <DialogContent> <DialogContentText id="alert-dialog-description"> - { this.state.message } + {this.state.message} </DialogContentText> </DialogContent> <DialogActions> @@ -168,7 +168,7 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { event.preventDefault(); this.setState({ busy: true }); - const token = await authenticationService.authenticateUser(this.state.email, this.state.password, this.state.scope); + const token = await authenticationService.authenticateUser(this.state.username, this.state.password, this.state.scope); this.props.dispatch(new UpdateAuthentication(token)); this.setState({ busy: false }); diff --git a/sdnr/wt/odlux/framework/src/views/test.tsx b/sdnr/wt/odlux/framework/src/views/test.tsx new file mode 100644 index 000000000..8efe357d1 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/views/test.tsx @@ -0,0 +1,861 @@ +import * as React from 'react'; + +import { withComponents, WithComponents } from '../utilities/withComponents'; +import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles'; + +import ExpansionPanel from '@material-ui/core/ExpansionPanel'; +import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'; +import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; + +import { MaterialTable, MaterialTableCtorType, ColumnType } from '../components/material-table'; +import { TreeView, ITreeItem, TreeViewCtorType } from '../components/material-ui/treeView'; +import { SvgIconProps } from '@material-ui/core/SvgIcon'; + +const styles = (theme: Theme) => createStyles({ + root: { + width: '100%', + }, + heading: { + fontSize: theme.typography.pxToRem(15), + fontWeight: theme.typography.fontWeightRegular, + }, +}); + +class SampleData { + _id: string; + index: number; + guid: string; + isActive: boolean; + balance: string; + age: number; + firstName: string; + lastName: string; + company: string; + email: string; + registered: string; + latitude: string; + longitude: string; +} + +// https://next.json-generator.com/NJ5Bv-v1I +const tableData: SampleData[] = [ + { + "_id": "5c0e18399919a5c43636fdf2", + "index": 0, + "guid": "48728d8e-8300-4d0f-b967-e2166d023066", + "isActive": false, + "balance": "$3,480.16", + "age": 33, + "firstName": "Brooke", + "lastName": "Morris", + "company": "ZORROMOP", + "email": "brooke.morris@zorromop.de", + "registered": "Sunday, February 11, 2018 2:55 PM", + "latitude": "-69.109379", + "longitude": "113.735639" + }, + { + "_id": "5c0e1839b61e3eeaf164259d", + "index": 1, + "guid": "28723570-1507-422e-b78c-924402371fb1", + "isActive": false, + "balance": "$1,305.01", + "age": 28, + "firstName": "Jolene", + "lastName": "Everett", + "company": "ZENCO", + "email": "jolene.everett@zenco.de", + "registered": "Saturday, December 8, 2018 5:17 PM", + "latitude": "13.683025", + "longitude": "85.101421" + }, + { + "_id": "5c0e1839e81f57913c5d2147", + "index": 2, + "guid": "e914dc5d-91a3-405d-ac48-aee6f0cd391a", + "isActive": true, + "balance": "$1,418.37", + "age": 28, + "firstName": "Elva", + "lastName": "Travis", + "company": "ZYTREK", + "email": "elva.travis@zytrek.de", + "registered": "Thursday, March 10, 2016 5:13 PM", + "latitude": "53.75862", + "longitude": "-67.784532" + }, + { + "_id": "5c0e1839bc9224a2b54c0f69", + "index": 3, + "guid": "88cbdce0-0bcc-4d16-83c3-3017690503c4", + "isActive": true, + "balance": "$1,709.60", + "age": 21, + "firstName": "Ellis", + "lastName": "Mcpherson", + "company": "DIGIPRINT", + "email": "ellis.mcpherson@digiprint.de", + "registered": "Sunday, December 21, 2014 5:25 AM", + "latitude": "46.486149", + "longitude": "-66.657067" + }, + { + "_id": "5c0e183951b51475db0f35d1", + "index": 4, + "guid": "c887ac86-7ba1-4eb6-9b47-e88a1bcb3713", + "isActive": true, + "balance": "$3,578.54", + "age": 25, + "firstName": "Marcia", + "lastName": "Rocha", + "company": "ZAPPIX", + "email": "marcia.rocha@zappix.de", + "registered": "Tuesday, June 16, 2015 11:21 AM", + "latitude": "-39.905461", + "longitude": "150.873895" + }, + { + "_id": "5c0e18398c5be8d362a578eb", + "index": 5, + "guid": "0d160697-9b5b-4941-9b5f-4ba3a7f97b49", + "isActive": true, + "balance": "$414.98", + "age": 32, + "firstName": "Lavonne", + "lastName": "Wilkins", + "company": "FARMAGE", + "email": "lavonne.wilkins@farmage.de", + "registered": "Monday, February 1, 2016 5:27 PM", + "latitude": "-16.839256", + "longitude": "-105.824746" + }, + { + "_id": "5c0e18399804086c836d7d56", + "index": 6, + "guid": "715a5f63-35b6-4903-a46e-ba584b005e64", + "isActive": false, + "balance": "$1,755.78", + "age": 32, + "firstName": "Wise", + "lastName": "Berg", + "company": "ZIZZLE", + "email": "wise.berg@zizzle.de", + "registered": "Saturday, March 28, 2015 1:40 AM", + "latitude": "51.15269", + "longitude": "65.795093" + }, + { + "_id": "5c0e18399c4d13538bcaf8c9", + "index": 7, + "guid": "7ee50269-23e8-499e-9a16-09f393d7600c", + "isActive": false, + "balance": "$342.52", + "age": 27, + "firstName": "Isabel", + "lastName": "Battle", + "company": "EZENTIA", + "email": "isabel.battle@ezentia.de", + "registered": "Thursday, June 7, 2018 12:16 AM", + "latitude": "-53.318152", + "longitude": "-153.516824" + }, + { + "_id": "5c0e18398d7fb9a4eceeffa2", + "index": 8, + "guid": "1e30c9ac-2297-4f16-83e6-9559b1ebe92c", + "isActive": true, + "balance": "$3,184.71", + "age": 36, + "firstName": "Lenora", + "lastName": "Crawford", + "company": "KIDGREASE", + "email": "lenora.crawford@kidgrease.de", + "registered": "Saturday, January 7, 2017 6:17 PM", + "latitude": "-72.431496", + "longitude": "9.413359" + }, + { + "_id": "5c0e18395837069ab6b79d00", + "index": 9, + "guid": "d04a02ed-5899-4729-a7e5-2d85b5d03973", + "isActive": true, + "balance": "$1,553.28", + "age": 35, + "firstName": "Sasha", + "lastName": "Bridges", + "company": "IDEALIS", + "email": "sasha.bridges@idealis.de", + "registered": "Sunday, February 4, 2018 7:02 PM", + "latitude": "8.095691", + "longitude": "-105.758195" + }, + { + "_id": "5c0e18390be19bf65acad180", + "index": 10, + "guid": "3a1a77e6-ef15-4598-8274-c68ac3bb922a", + "isActive": false, + "balance": "$3,587.96", + "age": 20, + "firstName": "Wilkins", + "lastName": "Beasley", + "company": "DIGIFAD", + "email": "wilkins.beasley@digifad.de", + "registered": "Monday, March 5, 2018 1:27 PM", + "latitude": "-88.062704", + "longitude": "149.95661" + }, + { + "_id": "5c0e1839ffbbad5c9954e49f", + "index": 11, + "guid": "97a56950-a08c-4e00-8002-ba2d5de4da5d", + "isActive": false, + "balance": "$1,997.80", + "age": 31, + "firstName": "Sullivan", + "lastName": "Mcclain", + "company": "EARTHMARK", + "email": "sullivan.mcclain@earthmark.de", + "registered": "Saturday, October 27, 2018 2:51 PM", + "latitude": "-81.86349", + "longitude": "-79.596991" + }, + { + "_id": "5c0e183914bd464d55e7325f", + "index": 12, + "guid": "294f6485-d0f9-4b25-b998-325ae90fa769", + "isActive": true, + "balance": "$1,405.46", + "age": 24, + "firstName": "Herminia", + "lastName": "Fischer", + "company": "ECOLIGHT", + "email": "herminia.fischer@ecolight.de", + "registered": "Thursday, January 16, 2014 4:48 PM", + "latitude": "48.224363", + "longitude": "11.08339" + }, + { + "_id": "5c0e183968ec2556d8f6566c", + "index": 13, + "guid": "16edfea4-7b37-4e54-868c-c369b413dd78", + "isActive": false, + "balance": "$3,440.67", + "age": 39, + "firstName": "Blanchard", + "lastName": "Blackwell", + "company": "GEOFORMA", + "email": "blanchard.blackwell@geoforma.de", + "registered": "Wednesday, July 30, 2014 4:07 AM", + "latitude": "-52.169297", + "longitude": "10.415879" + }, + { + "_id": "5c0e183939a0fc955f2d94da", + "index": 14, + "guid": "4ed454e2-dde1-4ab5-a434-4a82205ced2d", + "isActive": true, + "balance": "$1,883.27", + "age": 35, + "firstName": "Gayle", + "lastName": "Little", + "company": "AQUAZURE", + "email": "gayle.little@aquazure.de", + "registered": "Tuesday, December 12, 2017 5:08 PM", + "latitude": "-58.473236", + "longitude": "38.022269" + }, + { + "_id": "5c0e1839099f9221ccd968ac", + "index": 15, + "guid": "1d052fd4-7c54-45fb-b0db-7de1acc4262a", + "isActive": false, + "balance": "$2,601.94", + "age": 31, + "firstName": "Jocelyn", + "lastName": "Richards", + "company": "GINK", + "email": "jocelyn.richards@gink.de", + "registered": "Sunday, October 30, 2016 9:12 PM", + "latitude": "-43.489676", + "longitude": "2.557869" + }, + { + "_id": "5c0e183970f320f377321c3f", + "index": 16, + "guid": "45bca125-8831-48c3-b22b-29ae318e7096", + "isActive": false, + "balance": "$3,441.74", + "age": 34, + "firstName": "Berta", + "lastName": "Valentine", + "company": "ISOSPHERE", + "email": "berta.valentine@isosphere.de", + "registered": "Sunday, March 19, 2017 8:22 PM", + "latitude": "-40.188039", + "longitude": "-170.085092" + }, + { + "_id": "5c0e1839ab960bb0a9f4f392", + "index": 17, + "guid": "d7b5122a-94c9-423c-b799-1a8f8314b152", + "isActive": false, + "balance": "$56.39", + "age": 21, + "firstName": "Russell", + "lastName": "Powers", + "company": "TETAK", + "email": "russell.powers@tetak.de", + "registered": "Thursday, November 3, 2016 9:23 PM", + "latitude": "-51.610519", + "longitude": "-133.280363" + }, + { + "_id": "5c0e183998f0195404b9aaa4", + "index": 18, + "guid": "a043ba97-ea7e-48ce-bb15-18ee09fb393d", + "isActive": true, + "balance": "$1,503.57", + "age": 37, + "firstName": "Rosario", + "lastName": "Brennan", + "company": "VIAGRAND", + "email": "rosario.brennan@viagrand.de", + "registered": "Saturday, March 17, 2018 10:32 PM", + "latitude": "-43.773365", + "longitude": "47.58682" + }, + { + "_id": "5c0e1839bcb2a5cc567129ac", + "index": 19, + "guid": "de6d5d36-201e-4f87-9976-ed31f3160e42", + "isActive": false, + "balance": "$1,160.18", + "age": 29, + "firstName": "Anita", + "lastName": "Hodges", + "company": "TUBALUM", + "email": "anita.hodges@tubalum.de", + "registered": "Sunday, November 26, 2017 11:54 AM", + "latitude": "7.080244", + "longitude": "-9.970715" + }, + { + "_id": "5c0e18394b37e854a1ef371c", + "index": 20, + "guid": "9407113b-896a-4699-ac1b-363bc3c6f8ad", + "isActive": false, + "balance": "$34.81", + "age": 31, + "firstName": "Barrett", + "lastName": "Weaver", + "company": "DUOFLEX", + "email": "barrett.weaver@duoflex.de", + "registered": "Tuesday, November 3, 2015 9:31 AM", + "latitude": "40.30558", + "longitude": "-69.986664" + }, + { + "_id": "5c0e1839b5658f90e16a86e0", + "index": 21, + "guid": "81f894c4-c931-422d-a30e-593824d95bf9", + "isActive": true, + "balance": "$2,808.63", + "age": 26, + "firstName": "Baxter", + "lastName": "Chase", + "company": "BUNGA", + "email": "baxter.chase@bunga.de", + "registered": "Friday, October 28, 2016 7:10 AM", + "latitude": "-49.05652", + "longitude": "63.123535" + }, + { + "_id": "5c0e1839cb9462c9ecbb59af", + "index": 22, + "guid": "92e67862-4fdf-43af-a3ef-ef3edb8d6706", + "isActive": true, + "balance": "$3,552.71", + "age": 29, + "firstName": "Olga", + "lastName": "Kemp", + "company": "OHMNET", + "email": "olga.kemp@ohmnet.de", + "registered": "Saturday, March 26, 2016 11:51 AM", + "latitude": "-17.450481", + "longitude": "-13.945794" + }, + { + "_id": "5c0e18396f999c2b8ac731a9", + "index": 23, + "guid": "a682eaae-34f0-4973-b8a0-30972de0732b", + "isActive": false, + "balance": "$1,999.20", + "age": 21, + "firstName": "Ebony", + "lastName": "Le", + "company": "MULTRON", + "email": "ebony.le@multron.de", + "registered": "Friday, March 27, 2015 9:23 AM", + "latitude": "-70.380014", + "longitude": "173.20685" + }, + { + "_id": "5c0e18391cfb28263eb42db7", + "index": 24, + "guid": "f1cddb5f-0b89-453e-b0c9-8193a56cc610", + "isActive": true, + "balance": "$2,950.91", + "age": 30, + "firstName": "Norman", + "lastName": "Price", + "company": "COMVEX", + "email": "norman.price@comvex.de", + "registered": "Tuesday, August 21, 2018 11:17 PM", + "latitude": "86.501469", + "longitude": "159.545352" + }, + { + "_id": "5c0e18394a6be11128c7e5ca", + "index": 25, + "guid": "dadb738a-40fd-45b6-abac-023a803d95c2", + "isActive": true, + "balance": "$2,767.09", + "age": 25, + "firstName": "Sara", + "lastName": "Ruiz", + "company": "AUSTECH", + "email": "sara.ruiz@austech.de", + "registered": "Wednesday, June 20, 2018 6:34 AM", + "latitude": "86.784904", + "longitude": "-120.331325" + }, + { + "_id": "5c0e183974631549eda97cea", + "index": 26, + "guid": "b5c43ee5-14ed-4ab5-b3db-b31a8bb65ceb", + "isActive": true, + "balance": "$3,235.42", + "age": 32, + "firstName": "Holly", + "lastName": "Santos", + "company": "LOVEPAD", + "email": "holly.santos@lovepad.de", + "registered": "Thursday, November 22, 2018 9:26 PM", + "latitude": "-19.640066", + "longitude": "50.410992" + }, + { + "_id": "5c0e1839ab9b933881429d78", + "index": 27, + "guid": "94961092-65ca-41b9-bc69-3e40ce2cafc9", + "isActive": true, + "balance": "$2,106.34", + "age": 39, + "firstName": "Rachel", + "lastName": "Douglas", + "company": "DEMINIMUM", + "email": "rachel.douglas@deminimum.de", + "registered": "Sunday, April 9, 2017 3:55 AM", + "latitude": "31.395281", + "longitude": "-1.899514" + }, + { + "_id": "5c0e183937f743155859c5a9", + "index": 28, + "guid": "07d7ef18-bcef-483d-999e-0b3da4a7098b", + "isActive": true, + "balance": "$2,260.65", + "age": 40, + "firstName": "Reed", + "lastName": "Workman", + "company": "BUZZMAKER", + "email": "reed.workman@buzzmaker.de", + "registered": "Wednesday, May 28, 2014 3:44 PM", + "latitude": "23.789646", + "longitude": "106.938375" + }, + { + "_id": "5c0e1839f8f4b60beb28b7ed", + "index": 29, + "guid": "9b4952e5-aa0e-4919-9e17-7c357a297394", + "isActive": false, + "balance": "$702.99", + "age": 27, + "firstName": "Cochran", + "lastName": "Ware", + "company": "HIVEDOM", + "email": "cochran.ware@hivedom.de", + "registered": "Monday, October 16, 2017 5:51 AM", + "latitude": "85.953108", + "longitude": "124.590037" + }, + { + "_id": "5c0e1839342fbd54a88269df", + "index": 30, + "guid": "30937d5b-9514-4ebd-b628-2cfb5017fe41", + "isActive": false, + "balance": "$385.88", + "age": 35, + "firstName": "Cote", + "lastName": "Hess", + "company": "TERAPRENE", + "email": "cote.hess@teraprene.de", + "registered": "Thursday, March 15, 2018 4:42 PM", + "latitude": "81.38211", + "longitude": "64.516797" + }, + { + "_id": "5c0e18395b6dc85d73ce1fb3", + "index": 31, + "guid": "f34847da-7f96-4cd8-8d8a-b06c0eb0a8f2", + "isActive": true, + "balance": "$3,494.56", + "age": 27, + "firstName": "Daniels", + "lastName": "Ayala", + "company": "BESTO", + "email": "daniels.ayala@besto.de", + "registered": "Sunday, December 18, 2016 10:52 AM", + "latitude": "47.704227", + "longitude": "41.674767" + }, + { + "_id": "5c0e183974587cdccf30b13f", + "index": 32, + "guid": "fdbb6d83-0e47-4453-b8a7-b47f44e4164b", + "isActive": false, + "balance": "$2,087.38", + "age": 26, + "firstName": "Powers", + "lastName": "Drake", + "company": "GENESYNK", + "email": "powers.drake@genesynk.de", + "registered": "Saturday, September 29, 2018 12:24 AM", + "latitude": "40.580432", + "longitude": "110.940759" + }, + { + "_id": "5c0e18397b51245e971c58b8", + "index": 33, + "guid": "6adfe544-238b-4001-b2a6-f50ea3094da3", + "isActive": true, + "balance": "$3,566.22", + "age": 34, + "firstName": "Pacheco", + "lastName": "Ramsey", + "company": "ENVIRE", + "email": "pacheco.ramsey@envire.de", + "registered": "Friday, September 11, 2015 12:14 AM", + "latitude": "-30.691235", + "longitude": "69.343692" + }, + { + "_id": "5c0e18391ede9c0996fd09e7", + "index": 34, + "guid": "d190b32f-d33b-4c17-a18a-bb2f57e79ba7", + "isActive": false, + "balance": "$1,671.63", + "age": 32, + "firstName": "Mcintyre", + "lastName": "Chan", + "company": "ORBAXTER", + "email": "mcintyre.chan@orbaxter.de", + "registered": "Wednesday, May 7, 2014 7:11 PM", + "latitude": "7.380435", + "longitude": "70.955103" + }, + { + "_id": "5c0e1839fe48069c9c260fa9", + "index": 35, + "guid": "a41c064b-6bf4-4ba5-b229-9b657d286936", + "isActive": false, + "balance": "$24.02", + "age": 27, + "firstName": "Genevieve", + "lastName": "Sparks", + "company": "ZBOO", + "email": "genevieve.sparks@zboo.de", + "registered": "Saturday, December 16, 2017 2:51 PM", + "latitude": "-63.406337", + "longitude": "118.662621" + }, + { + "_id": "5c0e1839a7e8e76accf0803e", + "index": 36, + "guid": "3e71864d-4be5-418e-ace8-346c3d7a9c5f", + "isActive": true, + "balance": "$3,261.01", + "age": 30, + "firstName": "Powell", + "lastName": "Patterson", + "company": "GAZAK", + "email": "powell.patterson@gazak.de", + "registered": "Thursday, May 18, 2017 10:10 AM", + "latitude": "-10.428548", + "longitude": "64.979192" + }, + { + "_id": "5c0e183984b0320f1118a8b0", + "index": 37, + "guid": "ec5b292c-6efb-471b-9bf5-a47286e03515", + "isActive": false, + "balance": "$918.71", + "age": 37, + "firstName": "Tara", + "lastName": "Mcmillan", + "company": "GRAINSPOT", + "email": "tara.mcmillan@grainspot.de", + "registered": "Sunday, May 17, 2015 1:01 PM", + "latitude": "-13.519031", + "longitude": "67.931062" + }, + { + "_id": "5c0e183965875876835ccd79", + "index": 38, + "guid": "b7e97ffb-439a-4454-90af-7f5ebd565ebc", + "isActive": true, + "balance": "$574.99", + "age": 28, + "firstName": "Pennington", + "lastName": "Gallegos", + "company": "CEDWARD", + "email": "pennington.gallegos@cedward.de", + "registered": "Wednesday, September 26, 2018 6:01 AM", + "latitude": "-63.693261", + "longitude": "-38.352153" + }, + { + "_id": "5c0e183922505dd21be49009", + "index": 39, + "guid": "5187aa39-4357-462b-9508-3c537d26d70d", + "isActive": false, + "balance": "$2,447.08", + "age": 26, + "firstName": "Meagan", + "lastName": "Irwin", + "company": "SENTIA", + "email": "meagan.irwin@sentia.de", + "registered": "Saturday, April 2, 2016 4:39 PM", + "latitude": "1.051313", + "longitude": "-86.168315" + }, + { + "_id": "5c0e183900a9f7f896e5b3b1", + "index": 40, + "guid": "31889843-79e7-4636-9ca1-4eb5cbcb0ae3", + "isActive": true, + "balance": "$1,992.25", + "age": 22, + "firstName": "Kelly", + "lastName": "Cobb", + "company": "BOVIS", + "email": "kelly.cobb@bovis.de", + "registered": "Tuesday, August 9, 2016 5:36 PM", + "latitude": "-85.547579", + "longitude": "-89.794104" + }, + { + "_id": "5c0e18393b25b8552ff950e2", + "index": 41, + "guid": "0bf02edc-ca1b-4cfe-8356-b65881bdca11", + "isActive": true, + "balance": "$465.96", + "age": 27, + "firstName": "Angela", + "lastName": "Booker", + "company": "EQUICOM", + "email": "angela.booker@equicom.de", + "registered": "Thursday, July 30, 2015 1:39 AM", + "latitude": "-9.345395", + "longitude": "107.070665" + }, + { + "_id": "5c0e183955d747ebbe25437b", + "index": 42, + "guid": "6405e559-5849-4d12-ae4e-520f13b4dffe", + "isActive": true, + "balance": "$15.63", + "age": 28, + "firstName": "Carrie", + "lastName": "Mclean", + "company": "BOINK", + "email": "carrie.mclean@boink.de", + "registered": "Wednesday, February 1, 2017 1:50 PM", + "latitude": "72.287519", + "longitude": "-135.436286" + }, + { + "_id": "5c0e1839e9cfe1b28e31e7e6", + "index": 43, + "guid": "e49e7ca7-a6cc-4cdb-bebe-5a3b6ba931eb", + "isActive": true, + "balance": "$3,127.94", + "age": 33, + "firstName": "Callie", + "lastName": "Cooley", + "company": "MUSIX", + "email": "callie.cooley@musix.de", + "registered": "Wednesday, August 30, 2017 4:58 PM", + "latitude": "-38.954739", + "longitude": "-152.706424" + }, + { + "_id": "5c0e18391bafa0750ff4f280", + "index": 44, + "guid": "c245ffd3-4924-4dce-ae4a-f4cabf057b54", + "isActive": false, + "balance": "$1,320.36", + "age": 35, + "firstName": "Terry", + "lastName": "Bennett", + "company": "EXOTECHNO", + "email": "terry.bennett@exotechno.de", + "registered": "Friday, June 17, 2016 11:54 PM", + "latitude": "-48.946183", + "longitude": "32.53167" + }, + { + "_id": "5c0e1839e91b27fcce34b70f", + "index": 45, + "guid": "0860cb66-de4c-410e-8233-aeef5ee9d64e", + "isActive": false, + "balance": "$1,187.75", + "age": 30, + "firstName": "Phoebe", + "lastName": "Bartlett", + "company": "VORATAK", + "email": "phoebe.bartlett@voratak.de", + "registered": "Tuesday, July 25, 2017 2:57 AM", + "latitude": "-63.208957", + "longitude": "-91.209743" + }, + { + "_id": "5c0e183987e8a4e98415c8dd", + "index": 46, + "guid": "49219833-172c-4659-9192-d1116a5ca833", + "isActive": false, + "balance": "$3,225.24", + "age": 38, + "firstName": "Jordan", + "lastName": "Evans", + "company": "PHARMACON", + "email": "jordan.evans@pharmacon.de", + "registered": "Sunday, April 23, 2017 6:27 PM", + "latitude": "-59.454678", + "longitude": "67.251185" + }, + { + "_id": "5c0e183944979692cc1a3e48", + "index": 47, + "guid": "680c4d15-d539-4db9-8793-a2f6d3f354aa", + "isActive": false, + "balance": "$2,913.14", + "age": 28, + "firstName": "Goodman", + "lastName": "Cain", + "company": "CAXT", + "email": "goodman.cain@caxt.de", + "registered": "Tuesday, November 1, 2016 6:11 PM", + "latitude": "-30.187547", + "longitude": "-164.313273" + }, + { + "_id": "5c0e1839ef5312ac08e3cbc3", + "index": 48, + "guid": "85f5fa5d-b6b3-47c6-ad1b-faee10a4e1bd", + "isActive": true, + "balance": "$544.97", + "age": 27, + "firstName": "Aisha", + "lastName": "Oliver", + "company": "MINGA", + "email": "aisha.oliver@minga.de", + "registered": "Sunday, July 3, 2016 8:18 AM", + "latitude": "-21.527536", + "longitude": "141.029691" + }, + { + "_id": "5c0e1839c2e58f5da04f29fd", + "index": 49, + "guid": "e2ee9b25-5887-49a9-a1c6-17432154d266", + "isActive": true, + "balance": "$3,621.65", + "age": 31, + "firstName": "Erin", + "lastName": "Lester", + "company": "SLOFAST", + "email": "erin.lester@slofast.de", + "registered": "Saturday, February 20, 2016 5:13 AM", + "latitude": "-30.080798", + "longitude": "-1.291093" + } +]; + +const components = { + 'counter': 'demoApp.counter' +}; + +class TreeDemoItem implements ITreeItem { + title: string; + children?: TreeDemoItem[]; + disabled?: boolean; + icon?: React.ComponentType<SvgIconProps>; +} + +const treeData: TreeDemoItem[] = [ + { + title: "Erste Ebene", children: [ + { + title: "Zweite Ebene", children: [ + { title: "Dritte Ebene" }, + ] + }, + { title: "Zweite Ebene 2" }, + ] + }, + { title: "Erste Ebene 3" }, +]; + +const SampleDataMaterialTable = MaterialTable as MaterialTableCtorType<SampleData>; + +const SampleTree = TreeView as any as TreeViewCtorType<TreeDemoItem>; + +const TestComponent = (props: WithComponents<typeof components> & WithStyles<typeof styles>) => { + + return ( + <div> + <h2>About</h2> + <ExpansionPanel> + <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}> + <Typography className={props.classes.heading}>Client Side Table Demo</Typography> + </ExpansionPanelSummary> + <ExpansionPanelDetails> + <SampleDataMaterialTable rows={tableData} columns={ + [ + { property: "index", type: ColumnType.text, title: "Index", width: "80px", disableFilter: true, disableSorting: true }, + { property: "firstName", type: ColumnType.text, title: "First Name" }, + { property: "lastName", type: ColumnType.text, title: "Last Name" }, + { property: "age", type: ColumnType.numeric, title: "Age", width: "60px" }, + { property: "email", type: ColumnType.text, title: "eMail" }, + { property: "actions", type: ColumnType.custom, title: "Actions", customControl: ({ rowData }) => (<div>Button</div>) }, + ] + } idProperty={"_id"} title={"Customers 2018"} > + </SampleDataMaterialTable> + </ExpansionPanelDetails> + </ExpansionPanel> + <ExpansionPanel> + <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}> + <Typography className={props.classes.heading}>Tree Demo</Typography> + </ExpansionPanelSummary> + <ExpansionPanelDetails> + <SampleTree items={treeData} contentProperty={"title"} childrenProperty={"children"} useFolderIcons enableSearchBar /> + </ExpansionPanelDetails> + </ExpansionPanel> + </div> + ) +}; + +export const Test = withComponents(components)(withStyles(styles)(TestComponent)); +export default Test; |