aboutsummaryrefslogtreecommitdiffstats
path: root/sdnr/wt/odlux/framework/src
diff options
context:
space:
mode:
authorHerbert Eiselt <herbert.eiselt@highstreet-technologies.com>2019-03-28 19:00:35 +0100
committerHerbert Eiselt <herbert.eiselt@highstreet-technologies.com>2019-03-28 19:01:01 +0100
commit62e834802dae0bd15504785503060d7875c7b4ad (patch)
tree0b3d3ae0c62b279773a2aea3daebcad5f8ad78a2 /sdnr/wt/odlux/framework/src
parentf2bb490d9c82decbdb50c1e4db1be2f34b28d097 (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/src')
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-table/index.tsx887
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-table/utilities.ts43
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-ui/index.ts2
-rw-r--r--sdnr/wt/odlux/framework/src/components/navigationMenu.tsx6
-rw-r--r--sdnr/wt/odlux/framework/src/components/titleBar.tsx3
-rw-r--r--sdnr/wt/odlux/framework/src/design/default.ts61
-rw-r--r--sdnr/wt/odlux/framework/src/index.html46
-rw-r--r--sdnr/wt/odlux/framework/src/models/elasticSearch.ts3
-rw-r--r--sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts142
-rw-r--r--sdnr/wt/odlux/framework/src/views/about.tsx853
-rw-r--r--sdnr/wt/odlux/framework/src/views/frame.tsx12
-rw-r--r--sdnr/wt/odlux/framework/src/views/home.tsx27
-rw-r--r--sdnr/wt/odlux/framework/src/views/login.tsx50
-rw-r--r--sdnr/wt/odlux/framework/src/views/test.tsx861
14 files changed, 1525 insertions, 1471 deletions
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;