aboutsummaryrefslogtreecommitdiffstats
path: root/sdnr/wt/odlux/framework/src
diff options
context:
space:
mode:
Diffstat (limited to 'sdnr/wt/odlux/framework/src')
-rw-r--r--sdnr/wt/odlux/framework/src/actions/menuAction.ts31
-rw-r--r--sdnr/wt/odlux/framework/src/actions/websocketAction.ts8
-rw-r--r--sdnr/wt/odlux/framework/src/app.css1
-rw-r--r--sdnr/wt/odlux/framework/src/app.tsx38
-rw-r--r--sdnr/wt/odlux/framework/src/common/event.ts19
-rw-r--r--sdnr/wt/odlux/framework/src/components/errorDisplay.tsx50
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-table/index.tsx231
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx30
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx61
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-table/utilities.ts40
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-ui/index.ts10
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-ui/loader.tsx45
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-ui/panel.tsx56
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-ui/snackDisplay.tsx4
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-ui/toggleButton.tsx39
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-ui/treeView.tsx74
-rw-r--r--sdnr/wt/odlux/framework/src/components/navigationMenu.tsx138
-rw-r--r--sdnr/wt/odlux/framework/src/components/titleBar.tsx148
-rw-r--r--sdnr/wt/odlux/framework/src/design/default.ts21
-rw-r--r--sdnr/wt/odlux/framework/src/flux/action.ts18
-rw-r--r--sdnr/wt/odlux/framework/src/flux/connect.ts26
-rw-r--r--sdnr/wt/odlux/framework/src/flux/middleware.ts6
-rw-r--r--sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts25
-rw-r--r--sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts7
-rw-r--r--sdnr/wt/odlux/framework/src/models/elasticSearch.ts55
-rw-r--r--sdnr/wt/odlux/framework/src/models/errorInfo.ts1
-rw-r--r--sdnr/wt/odlux/framework/src/models/restService.ts18
-rw-r--r--sdnr/wt/odlux/framework/src/services/forceLogoutService.ts52
-rw-r--r--sdnr/wt/odlux/framework/src/services/notificationService.ts142
-rw-r--r--sdnr/wt/odlux/framework/src/services/restService.ts68
-rw-r--r--sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts63
-rw-r--r--sdnr/wt/odlux/framework/src/utilities/yangHelper.ts39
-rw-r--r--sdnr/wt/odlux/framework/src/views/frame.tsx142
-rw-r--r--sdnr/wt/odlux/framework/src/views/home.tsx19
-rw-r--r--sdnr/wt/odlux/framework/src/views/login.tsx22
35 files changed, 1152 insertions, 595 deletions
diff --git a/sdnr/wt/odlux/framework/src/actions/menuAction.ts b/sdnr/wt/odlux/framework/src/actions/menuAction.ts
new file mode 100644
index 000000000..ec0796587
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/actions/menuAction.ts
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import { Action } from '../flux/action';
+
+export class MenuAction extends Action {
+ constructor(public isOpen: boolean) {
+ super();
+ }
+}
+
+export class MenuClosedByUser extends Action {
+ constructor(public isClosed: boolean) {
+ super();
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/actions/websocketAction.ts b/sdnr/wt/odlux/framework/src/actions/websocketAction.ts
new file mode 100644
index 000000000..8512d59d5
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/actions/websocketAction.ts
@@ -0,0 +1,8 @@
+import { Action } from "../flux/action";
+
+
+export class SetWebsocketAction extends Action {
+ constructor(public isConnected: boolean) {
+ super();
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/app.css b/sdnr/wt/odlux/framework/src/app.css
index f3d3b7aab..356f36dd0 100644
--- a/sdnr/wt/odlux/framework/src/app.css
+++ b/sdnr/wt/odlux/framework/src/app.css
@@ -2,4 +2,5 @@ html, body, #app {
height: 100%;
padding: 0px;
margin: 0px;
+ font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
} \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/app.tsx b/sdnr/wt/odlux/framework/src/app.tsx
index 09c7a76e2..85b0dfb34 100644
--- a/sdnr/wt/odlux/framework/src/app.tsx
+++ b/sdnr/wt/odlux/framework/src/app.tsx
@@ -1,20 +1,20 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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. See the License for the specific language governing permissions and limitations under
- * the License.
- * ============LICENSE_END==========================================================================
- */
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
/******************************************************************************
* Copyright 2018 highstreet technologies GmbH
*
@@ -50,6 +50,8 @@ import { startRestService } from './services/restService';
import theme from './design/default';
import '!style-loader!css-loader!./app.css';
import { ReplaceAction } from './actions/navigationActions';
+import { startForceLogoutService } from './services/forceLogoutService';
+import { startNotificationService } from './services/notificationService';
declare module '@material-ui/core/styles/createMuiTheme' {
@@ -90,6 +92,8 @@ export const runApplication = () => {
startRestService(applicationStore);
startHistoryListener(applicationStore);
+ startForceLogoutService(applicationStore);
+ startNotificationService(applicationStore);
const App = (): JSX.Element => (
<ApplicationStoreProvider applicationStore={applicationStore} >
diff --git a/sdnr/wt/odlux/framework/src/common/event.ts b/sdnr/wt/odlux/framework/src/common/event.ts
index f71b0164a..913487911 100644
--- a/sdnr/wt/odlux/framework/src/common/event.ts
+++ b/sdnr/wt/odlux/framework/src/common/event.ts
@@ -1,4 +1,23 @@
/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+
+/**
* Represents an event.
* Events enable a class or object to notify other classes or objects when something of interest occurs.
* The class that sends (or invokes) the event is called the publisher and the classes that receive (or handle) the event are called subscribers.
diff --git a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx
index a20c592c5..4cf63e927 100644
--- a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx
+++ b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx
@@ -36,10 +36,10 @@ const styles = (theme: Theme) => createStyles({
justifyContent: "center",
},
paper: {
- width: theme.spacing.unit * 50,
+ width: theme.spacing(50),
backgroundColor: theme.palette.background.paper,
boxShadow: theme.shadows[5],
- padding: theme.spacing.unit * 4,
+ padding: theme.spacing(4),
},
card: {
minWidth: 275,
@@ -75,43 +75,47 @@ type ErrorDisplayProps = WithStyles<typeof styles> & Connect;
* Represents a compnent for formaing and displaying errors.
*/
class ErrorDisplayComponent extends React.Component<ErrorDisplayProps> {
+ constructor(props: ErrorDisplayProps) {
+ super(props);
+ }
+
render(): JSX.Element {
const { classes, state } = this.props;
const errorInfo = state.framework.applicationState.errors.length && state.framework.applicationState.errors[state.framework.applicationState.errors.length - 1];
return (
- <Modal className={ classes.modal }
+ <Modal className={classes.modal}
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
- open={ state.framework.applicationState.errors && state.framework.applicationState.errors.length > 0 }
- onClose={ () => this.props.dispatch(new ClearErrorInfoAction()) }
+ open={state.framework.applicationState.errors && state.framework.applicationState.errors.length > 0}
+ onClose={() => this.props.dispatch(new ClearErrorInfoAction())}
>
- { errorInfo &&
- <div className={ classes.paper }>
- <Card className={ classes.card }>
+ {errorInfo &&
+ <div className={classes.paper}>
+ <Card className={classes.card}>
<CardContent>
- <Typography className={ classes.title } color="textSecondary">
- Something went wrong.
+ <Typography className={classes.title} color="textSecondary">
+ {errorInfo.title != null ? errorInfo.title : "Something went wrong."}
</Typography>
- <Typography variant="headline" component="h2">
- { errorInfo.error && errorInfo.error.toString() }
+ <Typography variant="h5" component="h2">
+ {errorInfo.error && errorInfo.error.toString()}
</Typography>
- <Typography className={ classes.pos } color="textSecondary">
- { errorInfo.message && errorInfo.message .toString() }
+ <Typography className={classes.pos} color="textSecondary">
+ {errorInfo.message && errorInfo.message.toString()}
</Typography>
<Typography component="p">
- { errorInfo.info && errorInfo.info.componentStack && errorInfo.info.componentStack.split('\n').map(line => {
- return [line, <br />];
- }) }
- { errorInfo.info && errorInfo.info.extra && errorInfo.info.extra.split('\n').map(line => {
- return [line, <br />];
- }) }
+ {errorInfo.info && errorInfo.info.componentStack && errorInfo.info.componentStack.split('\n').map(line => {
+ return [line, <br />];
+ })}
+ {errorInfo.info && errorInfo.info.extra && errorInfo.info.extra.split('\n').map(line => {
+ return [line, <br />];
+ })}
</Typography>
</CardContent>
<CardActions>
- <Button size="small" onClick={ () => this.props.dispatch(new RemoveErrorInfoAction(errorInfo)) } >Close</Button>
- </CardActions>
+ <Button size="small" onClick={() => this.props.dispatch(new RemoveErrorInfoAction(errorInfo))} >Close</Button>
+ </CardActions>
</Card>
- </div> || null
+ </div> || <div></div>
}
</Modal>
);
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 d89ced23a..3e31c5e03 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
@@ -33,19 +33,21 @@ import { EnhancedTableFilter } from './tableFilter';
import { ColumnModel, ColumnType } from './columnModel';
import { Omit } from '@material-ui/core';
import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon';
+import { replaceHyphen } from '../../utilities/yangHelper';
+import { string } from 'prop-types';
export { ColumnModel, ColumnType } from './columnModel';
-type propType = string | number | null | undefined | (string|number)[];
+type propType = string | number | null | undefined | (string | number)[];
type dataType = { [prop: string]: propType };
-type resultType<TData = dataType> = { page: number, rowCount: number, rows: TData[] };
+type resultType<TData = dataType> = { page: number, total: 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>>;
+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] || "") ) {
+ if ((b[orderBy] || "") < (a[orderBy] || "")) {
return -1;
}
- if ((b[orderBy] || "") > (a[orderBy] || "") ) {
+ if ((b[orderBy] || "") > (a[orderBy] || "")) {
return 1;
}
return 0;
@@ -68,7 +70,7 @@ function getSorting(order: 'asc' | 'desc' | null, orderBy: string) {
const styles = (theme: Theme) => createStyles({
root: {
width: '100%',
- marginTop: theme.spacing.unit * 3,
+ marginTop: theme.spacing(3),
},
table: {
minWidth: 1020,
@@ -83,7 +85,7 @@ export type MaterialTableComponentState<TData = {}> = {
orderBy: string | null;
selected: any[] | null;
rows: TData[];
- rowCount: number;
+ total: number;
page: number;
rowsPerPage: number;
loading: boolean;
@@ -95,18 +97,19 @@ export type TableApi = { forceRefresh?: () => Promise<void> };
type MaterialTableComponentBaseProps<TData> = WithStyles<typeof styles> & {
columns: ColumnModel<TData>[];
- idProperty: keyof TData | ((data: TData) => React.Key );
+ idProperty: keyof TData | ((data: TData) => React.Key);
+ tableId?: string;
title?: string;
enableSelection?: boolean;
disableSorting?: boolean;
disableFilter?: boolean;
- customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, onClick: () => void }[];
+ 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 & {
+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;
@@ -152,7 +155,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
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,
+ total: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.length || 0,
page,
rowsPerPage,
};
@@ -167,58 +170,58 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
}
render(): JSX.Element {
const { classes, columns } = this.props;
- const { rows, rowCount, order, orderBy, selected, rowsPerPage, page, showFilter, filter } = this.state;
+ const { rows, total: 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 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">
+ <Paper className={classes.root}>
+ <TableToolbar tableId={this.props.tableId} 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 }
+ 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
+ {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) }
+ onClick={event => this.handleClick(event, entry, entryId)}
role="checkbox"
- aria-checked={ isSelected }
- tabIndex={ -1 }
- key={ entryId }
- selected={ isSelected }
+ aria-checked={isSelected}
+ tabIndex={-1}
+ key={entryId}
+ selected={isSelected}
>
- { this.props.enableSelection
- ? <TableCell padding="checkbox" style={ { width: "50px" } }>
- <Checkbox checked={ isSelected } />
+ {this.props.enableSelection
+ ? <TableCell padding="checkbox" style={{ width: "50px" }}>
+ <Checkbox checked={isSelected} />
</TableCell>
- : null
+ : null
}
{
this.props.columns.map(
col => {
- const style = col.width ? { width: col.width } : { };
+ 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 } />
+ <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}>{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>
@@ -228,29 +231,29 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
}
</TableRow>
);
- }) }
- { emptyRows > 0 && (
- <TableRow style={ { height: 49 * emptyRows } }>
- <TableCell colSpan={ this.props.columns.length } />
+ })}
+ {emptyRows > 0 && (
+ <TableRow style={{ height: 49 * emptyRows }}>
+ <TableCell colSpan={this.props.columns.length} />
</TableRow>
- ) }
+ )}
</TableBody>
</Table>
</div>
<TablePagination
- rowsPerPageOptions={[5, 10, 20, 50] }
+ rowsPerPageOptions={[5, 10, 20, 50]}
component="div"
- count={ rowCount }
- rowsPerPage={ rowsPerPage }
- page={ page }
- backIconButtonProps={ {
+ count={rowCount}
+ rowsPerPage={rowsPerPage}
+ page={page}
+ backIconButtonProps={{
'aria-label': 'Previous Page',
- } }
- nextIconButtonProps={ {
+ }}
+ nextIconButtonProps={{
'aria-label': 'Next Page',
- } }
- onChangePage={ this.onHandleChangePage }
- onChangeRowsPerPage={ this.onHandleChangeRowsPerPage }
+ }}
+ onChangePage={this.onHandleChangePage}
+ onChangeRowsPerPage={this.onHandleChangeRowsPerPage}
/>
</Paper>
);
@@ -261,7 +264,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
return {
...state,
rows: props.rows,
- rowCount: props.rowCount,
+ total: props.total,
orderBy: props.orderBy,
order: props.order,
filter: props.filter,
@@ -281,9 +284,11 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
return state;
}
- private static updateRows(props: MaterialTableComponentPropsWithRows, state: MaterialTableComponentState): { rows: {}[], rowCount: number } {
+ private static updateRows(props: MaterialTableComponentPropsWithRows, state: MaterialTableComponentState): { rows: {}[], total: number, page: number } {
+
+ const { page, rowsPerPage, order, orderBy, filter } = state;
+
try {
- const { page, rowsPerPage, order, orderBy, filter } = state;
let data: dataType[] = props.rows || [];
let filtered = false;
if (state.showFilter) {
@@ -292,25 +297,84 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
filtered = filtered || exp !== undefined;
data = exp !== undefined ? data.filter((val) => {
const value = val[prop];
- return (value == exp) || (value && value.toString().indexOf(String(exp)) > -1);
+
+ if (value) {
+
+ if (typeof exp === 'boolean') {
+ return value == exp;
+
+ } else if (typeof exp === 'string') {
+
+ const valueAsString = value.toString();
+ if (exp.length === 0) return value;
+
+ const regex = new RegExp("\\*", "g");
+ const regex2 = new RegExp("\\?", "g");
+
+ const countStar = (exp.match(regex) || []).length;
+ const countQuestionmarks = (exp.match(regex2) || []).length;
+
+ if (countStar > 0 || countQuestionmarks > 0) {
+ let editableExpression = exp;
+
+ if (!exp.startsWith('*')) {
+ editableExpression = '^' + exp;
+ }
+
+ if (!exp.endsWith('*')) {
+ editableExpression = editableExpression + '$';
+ }
+
+ const expressionAsRegex = editableExpression.replace(/\*/g, ".*").replace(/\?/g, ".");
+
+ return valueAsString.match(new RegExp(expressionAsRegex, "g"));
+ }
+ else if (exp.includes('>=')) {
+ return Number(valueAsString) >= Number(exp.replace('>=', ''));
+ } else if (exp.includes('<=')) {
+ return Number(valueAsString) <= Number(exp.replace('<=', ''));
+ } else
+ if (exp.includes('>')) {
+ return Number(valueAsString) > Number(exp.replace('>', ''));
+ } else if (exp.includes('<')) {
+ return Number(valueAsString) < Number(exp.replace('<', ''));
+ }
+ }
+ }
+
+ return (value == exp)
}) : data;
});
}
const rowCount = data.length;
- data = (orderBy && order
- ? stableSort(data, getSorting(order, orderBy))
- : data).slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
+ if (page > 0 && rowsPerPage * page > rowCount) { //if result is smaller than the currently shown page, new search and repaginate
+ let newPage = Math.floor(rowCount / rowsPerPage);
+ return {
+ rows: data,
+ total: rowCount,
+ page: newPage
+ };
+ } else {
+ data = (orderBy && order
+ ? stableSort(data, getSorting(order, orderBy))
+ : data).slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
+
+ return {
+ rows: data,
+ total: rowCount,
+ page: page
+ };
+ }
- return {
- rows: data,
- rowCount
- };
- } catch{
+
+ } catch (e) {
+ console.error(e);
return {
rows: [],
- rowCount: 0
+ total: 0,
+ page: page
}
}
}
@@ -323,7 +387,8 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
);
this.setState(response);
} else {
- this.setState(MaterialTableComponent.updateRows(this.props, this.state));
+ let updateResult = MaterialTableComponent.updateRows(this.props, this.state);
+ this.setState(updateResult);
}
}
@@ -361,7 +426,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
handleSelectAllClick: () => {};
- private onHandleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, page: number) => {
+ private onHandleChangePage = (event: any | null, page: number) => {
if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
this.props.onHandleChangePage(page);
return;
@@ -390,12 +455,12 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
return (selectedIndex > -1);
}
- private handleClick(event: React.MouseEvent<HTMLTableRowElement>, rowData: TData, id: string | number): void {
+ private handleClick(event: any, rowData: TData, id: string | number): void {
if (this.props.onHandleClick instanceof Function) {
this.props.onHandleClick(event, rowData);
return;
}
- if (!this.props.enableSelection){
+ if (!this.props.enableSelection) {
return;
}
let selected = this.state.selected || [];
@@ -416,20 +481,26 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
});
}
+
private exportToCsv = async () => {
let file;
let data: dataType[] | null = null;
let csv: string[] = [];
-
if (isMaterialTableComponentPropsWithRequestData(this.props)) {
+ // table with extra request handler
this.setState({ loading: true });
const result = await Promise.resolve(
- this.props.onRequestData( 0, 1000, this.state.orderBy, this.state.order, this.state.showFilter && this.state.filter || {})
+ this.props.onRequestData(0, 1000, this.state.orderBy, this.state.order, this.state.showFilter && this.state.filter || {})
);
data = result.rows;
this.setState({ loading: true });
- } else {
+ } else if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
+ // table with generated handlers note: exports data shown on current page
+ data = this.props.rows;
+ }
+ else {
+ // table with local data
data = MaterialTableComponent.updateRows(this.props, this.state).rows;
}
@@ -438,7 +509,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
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.
+ 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);
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx b/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx
index 0356aa225..737ea85f9 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx
@@ -33,7 +33,7 @@ const styles = (theme: Theme) => createStyles({
flexWrap: 'wrap',
},
input: {
- margin: theme.spacing.unit,
+ margin: theme.spacing(1),
},
});
@@ -45,7 +45,7 @@ interface IEnhancedTableFilterComponentProps extends WithStyles<typeof styles> {
}
class EnhancedTableFilterComponent extends React.Component<IEnhancedTableFilterComponentProps> {
- createFilterHandler = (property: string) => (event: React.ChangeEvent<HTMLInputElement|HTMLSelectElement|HTMLTextAreaElement>) => {
+ createFilterHandler = (property: string) => (event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
this.props.onFilterChanged && this.props.onFilterChanged(property, event.target.value);
};
@@ -53,33 +53,33 @@ class EnhancedTableFilterComponent extends React.Component<IEnhancedTableFilterC
const { columns, filter, classes } = this.props;
return (
<TableRow>
- { this.props.enableSelection
- ? <TableCell padding="checkbox" style={ { width: "50px" } }>
- </TableCell>
- : null
- }
- { columns.map(col => {
+ {this.props.enableSelection
+ ? <TableCell padding="checkbox" style={{ width: "50px" }}>
+ </TableCell>
+ : null
+ }
+ {columns.map(col => {
const style = col.width ? { width: col.width } : {};
return (
<TableCell
- key={ col.property }
- padding={ col.disablePadding ? 'none' : 'default' }
- style={ style }
+ key={col.property}
+ padding={col.disablePadding ? 'none' : 'default'}
+ style={style}
>
- { col.disableFilter || (col.type === ColumnType.custom)
+ {col.disableFilter || (col.type === ColumnType.custom)
? null
: (col.type === ColumnType.boolean)
? <Select className={classes.input} value={filter[col.property] !== undefined ? filter[col.property] : ''} onChange={this.createFilterHandler(col.property)} inputProps={{ name: `${col.property}-bool`, id: `${col.property}-bool` }} >
<MenuItem value={undefined}>
<em>None</em>
</MenuItem>
- <MenuItem value={true as any as string}>{ col.labels ? col.labels["true"]:"true"}</MenuItem>
- <MenuItem value={false as any as string}>{ col.labels ? col.labels["false"] : "false"}</MenuItem>
+ <MenuItem value={true as any as string}>{col.labels ? col.labels["true"] : "true"}</MenuItem>
+ <MenuItem value={false as any as string}>{col.labels ? col.labels["false"] : "false"}</MenuItem>
</Select>
: <Input className={classes.input} inputProps={{ 'aria-label': 'Filter' }} value={filter[col.property] || ''} onChange={this.createFilterHandler(col.property)} />}
</TableCell>
);
- }, this) }
+ }, this)}
</TableRow>
);
}
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx b/sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx
index 80d38ab52..a4080b51b 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx
@@ -32,7 +32,7 @@ import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon';
const styles = (theme: Theme) => createStyles({
root: {
- paddingRight: theme.spacing.unit,
+ paddingRight: theme.spacing(1),
},
highlight:
theme.palette.type === 'light'
@@ -65,6 +65,7 @@ const styles = (theme: Theme) => createStyles({
interface ITableToolbarComponentProps extends WithStyles<typeof styles> {
numSelected: number | null;
title?: string;
+ tableId?: string;
customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, onClick: () => void }[];
onToggleFilter: () => void;
onExportToCsv: () => void;
@@ -79,7 +80,7 @@ class TableToolbarComponent extends React.Component<ITableToolbarComponentProps,
};
}
- private handleMenu = (event: React.MouseEvent<HTMLElement>) => {
+ private handleMenu = (event: React.MouseEvent<HTMLElement>) => {
this.setState({ anchorEl: event.currentTarget });
};
@@ -89,55 +90,55 @@ class TableToolbarComponent extends React.Component<ITableToolbarComponentProps,
render() {
const { numSelected, classes } = this.props;
const open = !!this.state.anchorEl;
-
+ const buttonPrefix = this.props.tableId !== undefined ? this.props.tableId + '-' : '';
return (
- <Toolbar className={ `${ classes.root } ${ numSelected && numSelected > 0 ? classes.highlight : '' } ` } >
- <div className={ classes.title }>
- { numSelected && numSelected > 0 ? (
- <Typography color="inherit" variant="subheading">
- { numSelected } selected
+ <Toolbar className={`${classes.root} ${numSelected && numSelected > 0 ? classes.highlight : ''} `} >
+ <div className={classes.title}>
+ {numSelected && numSelected > 0 ? (
+ <Typography color="inherit" variant="subtitle1">
+ {numSelected} selected
</Typography>
) : (
- <Typography variant="headline" id="tableTitle">
- { this.props.title || null }
+ <Typography variant="h5" id="tableTitle">
+ {this.props.title || null}
</Typography>
- ) }
+ )}
</div>
- <div className={ classes.spacer } />
- <div className={ classes.actions }>
- { this.props.customActionButtons
- ? this.props.customActionButtons.map((action, ind) =>(
- <Tooltip key={ `custom-action-${ ind }`} title={action.tooltip}>
- <IconButton aria-label={ `custom-action-${ind}` } onClick={() => action.onClick() }>
- <action.icon />
- </IconButton>
- </Tooltip>
+ <div className={classes.spacer} />
+ <div className={classes.actions}>
+ {this.props.customActionButtons
+ ? this.props.customActionButtons.map((action, ind) => (
+ <Tooltip key={`custom-action-${ind}`} title={action.tooltip}>
+ <IconButton aria-label={buttonPrefix + `custom-action-${ind}`} onClick={() => action.onClick()}>
+ <action.icon />
+ </IconButton>
+ </Tooltip>
))
- : null }
- { numSelected && numSelected > 0 ? (
+ : null}
+ {numSelected && numSelected > 0 ? (
<Tooltip title="Delete">
- <IconButton aria-label="Delete">
+ <IconButton aria-label={buttonPrefix + "delete"}>
<DeleteIcon />
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
- <IconButton aria-label="Filter list" onClick={ () => { this.props.onToggleFilter && this.props.onToggleFilter() } }>
+ <IconButton aria-label={buttonPrefix + "filter-list"} onClick={() => { this.props.onToggleFilter && this.props.onToggleFilter() }}>
<FilterListIcon />
</IconButton>
</Tooltip>
- ) }
+ )}
<Tooltip title="Actions">
<IconButton color="inherit"
- aria-owns={ open ? 'menu-appbar' : undefined }
+ aria-owns={open ? 'menu-appbar' : undefined}
aria-haspopup="true"
- onClick={ this.handleMenu } >
+ onClick={this.handleMenu} >
<MoreIcon />
</IconButton>
</Tooltip>
- <Menu id="menu-appbar" anchorEl={ this.state.anchorEl } anchorOrigin={ { vertical: 'top', horizontal: 'right' } }
- transformOrigin={ { vertical: 'top', horizontal: 'right' } } open={ open } onClose={ this.handleClose } >
- <MenuItem onClick={ this.props.onExportToCsv }>Export as CSV</MenuItem>
+ <Menu id="menu-appbar" anchorEl={this.state.anchorEl} anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
+ transformOrigin={{ vertical: 'top', horizontal: 'right' }} open={open} onClose={this.handleClose} >
+ <MenuItem onClick={this.props.onExportToCsv}>Export as CSV</MenuItem>
</Menu>
</div>
</Toolbar>
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 43c3b5e35..6e8902c07 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts
+++ b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts
@@ -27,7 +27,7 @@ export interface IExternalTableState<TData> {
orderBy: string | null;
selected: any[] | null;
rows: TData[];
- rowCount: number;
+ total: number;
page: number;
rowsPerPage: number;
loading: boolean;
@@ -68,13 +68,13 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState
}
class SetPreFilterChangedAction extends TableAction {
- constructor(public preFilter: {[key: string]: string}) {
+ constructor(public preFilter: { [key: string]: string }) {
super();
}
}
class SetFilterChangedAction extends TableAction {
- constructor (public filter: { [key: string]: string }) {
+ constructor(public filter: { [key: string]: string }) {
super();
}
}
@@ -92,7 +92,7 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState
}
class SetResultAction extends TableAction {
- constructor(public result: { page: number, rowCount: number, rows: TData[] }) {
+ constructor(public result: { page: number, total: number, rows: TData[] }) {
super();
}
}
@@ -105,7 +105,7 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState
orderBy: null,
selected: null,
rows: [],
- rowCount: 0,
+ total: 0,
page: 0,
rowsPerPage: 10,
loading: false,
@@ -126,14 +126,14 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState
...state,
loading: false,
rows: action.result.rows,
- rowCount: action.result.rowCount,
+ total: action.result.total,
page: action.result.page,
}
} else if (action instanceof RequestSortAction) {
state = {
...state,
loading: true,
- orderBy : state.orderBy === action.orderBy && state.order === 'desc' ? null : action.orderBy ,
+ orderBy: state.orderBy === action.orderBy && state.order === 'desc' ? null : action.orderBy,
order: state.orderBy === action.orderBy && state.order === 'asc' ? 'desc' : 'asc',
}
} else if (action instanceof SetShowFilterAction) {
@@ -176,9 +176,23 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState
const reloadAction = (dispatch: Dispatch, getAppState: () => IApplicationStoreState) => {
dispatch(new RefreshAction());
const ownState = selectState(getAppState());
- 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));
+ const filter = { ...ownState.preFilter, ...(ownState.showFilter && ownState.filter || {}) };
+ Promise.resolve(callback(ownState.page, ownState.rowsPerPage, ownState.orderBy, ownState.order, filter)).then(result => {
+
+ if (ownState.page > 0 && ownState.rowsPerPage * ownState.page > result.total) { //if result is smaller than the currently shown page, new search and repaginate
+
+ let newPage = Math.floor(result.total / ownState.rowsPerPage);
+
+ Promise.resolve(callback(newPage, ownState.rowsPerPage, ownState.orderBy, ownState.order, filter)).then(result1 => {
+ dispatch(new SetResultAction(result1));
+ });
+
+
+ } else {
+ dispatch(new SetResultAction(result));
+ }
+
+
}).catch(error => new AddErrorInfoAction(error));
};
@@ -186,8 +200,8 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState
return {
onPreFilterChanged: (preFilter: { [key: string]: string }) => {
dispatch(new SetPreFilterChangedAction(preFilter));
- (!skipRefresh) && dispatch(reloadAction);
- }
+ (!skipRefresh) && dispatch(reloadAction);
+ }
};
}
@@ -236,7 +250,7 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState
const createProperties = (state: IApplicationStoreState) => {
return {
...selectState(state)
- }
+ }
}
return {
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 c756f7f07..e0e3fc943 100644
--- a/sdnr/wt/odlux/framework/src/components/material-ui/index.ts
+++ b/sdnr/wt/odlux/framework/src/components/material-ui/index.ts
@@ -15,8 +15,8 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-export { ListItemLink } from './listItemLink';
-export { Panel } from './panel';
-export { ToggleButton, ToggleButtonClassKey } from './toggleButton';
-export { TreeView, ITreeItem, TreeViewCtorType} from './treeView';
-
+export { ListItemLink } from './listItemLink';
+export { Panel } from './panel';
+export { ToggleButton, ToggleButtonClassKey } from './toggleButton';
+export { TreeView, ITreeItem, TreeViewCtorType} from './treeView';
+export { Loader } from './loader';
diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/loader.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/loader.tsx
new file mode 100644
index 000000000..5ab2fd415
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/components/material-ui/loader.tsx
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+
+import * as React from "react";
+
+import { WithStyles, withStyles, createStyles, Theme } from '@material-ui/core/styles';
+
+const styles = (theme: Theme) => createStyles({
+ "@keyframes spin": {
+ "0%": { transform: "rotate(0deg)" },
+ "100%": { transform: "rotate(360deg)" },
+ },
+ loader: {
+ border: `16px solid ${theme.palette.grey.A200}`,
+ borderTop: `16px solid ${theme.palette.secondary.main}`,
+ borderRadius: "50%",
+ width: "120px",
+ height: "120px",
+ animation: "$spin 2s linear infinite",
+ }
+});
+
+const LoaderComponent: React.FC<WithStyles<typeof styles>> = (props) => {
+ return (
+ <div className={props.classes.loader} />
+ );
+};
+
+export const Loader = withStyles(styles)(LoaderComponent);
diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/panel.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/panel.tsx
index 9627a749b..378d48592 100644
--- a/sdnr/wt/odlux/framework/src/components/material-ui/panel.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-ui/panel.tsx
@@ -1,20 +1,20 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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. See the License for the specific language governing permissions and limitations under
- * the License.
- * ============LICENSE_END==========================================================================
- */
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
import * as React from 'react';
import { withStyles, Theme, WithStyles, createStyles } from '@material-ui/core/styles';
@@ -32,8 +32,8 @@ const styles = (theme: Theme) => createStyles({
detail: {
// background: theme.palette.background.paper,
// color: theme.palette.text.primary,
- position: "relative",
- display: 'flex',
+ position: "relative",
+ display: 'flex',
flexDirection: 'column'
},
text: {
@@ -53,18 +53,18 @@ type PanalProps = WithStyles<typeof styles> & {
const PanelComponent: React.SFC<PanalProps> = (props) => {
const { classes, activePanel, onToggle } = props;
return (
- <ExpansionPanel className={ classes.accordion } expanded={ activePanel === props.panelId } onChange={ () => onToggle(props.panelId) } >
- <ExpansionPanelSummary expandIcon={ <ExpandMoreIcon /> }>
- <Typography className={ classes.text } >{ props.title }</Typography>
+ <ExpansionPanel className={classes.accordion} expanded={activePanel === props.panelId} onChange={() => onToggle(props.panelId)} >
+ <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography className={classes.text} >{props.title}</Typography>
</ExpansionPanelSummary>
- <ExpansionPanelDetails className={ classes.detail }>
- { props.children }
+ <ExpansionPanelDetails className={classes.detail}>
+ {props.children}
</ExpansionPanelDetails>
- { props.customActionButtons
+ {props.customActionButtons
? <ExpansionPanelActions>
- { props.customActionButtons }
- </ExpansionPanelActions>
- : null }
+ {props.customActionButtons}
+ </ExpansionPanelActions>
+ : null}
</ExpansionPanel>
);
};
diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/snackDisplay.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/snackDisplay.tsx
index 610376107..437784ce5 100644
--- a/sdnr/wt/odlux/framework/src/components/material-ui/snackDisplay.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-ui/snackDisplay.tsx
@@ -21,7 +21,7 @@ import { IApplicationStoreState } from '../../store/applicationStore';
import { Connect, connect, IDispatcher } from '../../flux/connect';
import { RemoveSnackbarNotification } from '../../actions/snackbarActions';
-import { InjectedNotistackProps, withSnackbar } from 'notistack';
+import { WithSnackbarProps, withSnackbar } from 'notistack';
const mapProps = (state: IApplicationStoreState) => ({
notifications: state.framework.applicationState.snackBars
@@ -33,7 +33,7 @@ const mapDispatch = (dispatcher: IDispatcher) => ({
}
});
-type DisplaySnackbarsComponentProps = Connect<typeof mapProps, typeof mapDispatch> & InjectedNotistackProps;
+type DisplaySnackbarsComponentProps = Connect<typeof mapProps, typeof mapDispatch> & WithSnackbarProps;
class DisplaySnackbarsComponent extends React.Component<DisplaySnackbarsComponentProps> {
private displayed: number[] = [];
diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/toggleButton.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/toggleButton.tsx
index fb10ca941..1a29d6970 100644
--- a/sdnr/wt/odlux/framework/src/components/material-ui/toggleButton.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-ui/toggleButton.tsx
@@ -1,20 +1,20 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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. See the License for the specific language governing permissions and limitations under
- * the License.
- * ============LICENSE_END==========================================================================
- */
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
import * as React from 'react';
import classNames from 'classnames';
@@ -30,7 +30,7 @@ export const styles = (theme: Theme) => createStyles({
height: 32,
minWidth: 48,
margin: 0,
- padding: `${theme.spacing.unit - 4}px ${theme.spacing.unit * 1.5}px`,
+ padding: `${theme.spacing(1 - 4)}px ${theme.spacing(1.5)}px`,
borderRadius: 2,
willChange: 'opacity',
color: fade(theme.palette.action.active, 0.38),
@@ -119,6 +119,7 @@ class ToggleButtonComponent extends React.Component<IToggleButtonProps> {
handleChange = (event: React.FormEvent<HTMLElement>) => {
const { onChange, onClick, value } = this.props;
+ event.stopPropagation();
if (onClick) {
onClick(event, value);
if (event.isDefaultPrevented()) {
@@ -129,6 +130,7 @@ class ToggleButtonComponent extends React.Component<IToggleButtonProps> {
if (onChange) {
onChange(event, value);
}
+ event.preventDefault();
};
render() {
@@ -157,6 +159,7 @@ class ToggleButtonComponent extends React.Component<IToggleButtonProps> {
disabled={disabled}
focusRipple={!disableFocusRipple}
onClick={this.handleChange}
+ href="#"
{...other}
>
<span className={classes.label}>{children}</span>
diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/treeView.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/treeView.tsx
index 7f2f73926..e4eb3a794 100644
--- a/sdnr/wt/odlux/framework/src/components/material-ui/treeView.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-ui/treeView.tsx
@@ -103,21 +103,21 @@ class TreeViewComponent<TData extends ITreeItem> extends React.Component<TreeVie
...this.props.style
},
search: {
- padding: `0px ${ this.props.theme.spacing.unit }px`
+ padding: `0px ${this.props.theme.spacing(1)}px`
}
};
return (
- <div style={ styles.root }>
- { children }
- { enableSearchBar && <TextField label={ "Search" } fullWidth={ true } style={ styles.search } value={ searchTerm } onChange={ this.onChangeSearchText } /> || null }
+ <div style={styles.root}>
+ {children}
+ {enableSearchBar && <TextField label={"Search"} fullWidth={true} style={styles.search} value={searchTerm} onChange={this.onChangeSearchText} /> || null}
<List>
- { this.renderItems(items, searchTerm && searchTerm.toLowerCase()) }
+ {this.renderItems(items, searchTerm && searchTerm.toLowerCase())}
</List>
</div>
);
}
- private itemIndex: number = 0;
+ private itemIndex: number = 0;
private renderItems = (items: TData[], searchTerm: string | undefined, depth: number = 1) => {
return items.reduce((acc, item) => {
@@ -144,7 +144,7 @@ class TreeViewComponent<TData extends ITreeItem> extends React.Component<TreeVie
private renderItem = (item: TData, searchTerm: string | undefined, depth: number, isFolder: boolean, expanded: boolean): JSX.Element | null => {
const styles = {
item: {
- paddingLeft: (((this.props.depthOffset || 0) + depth) * this.props.theme.spacing.unit * 3),
+ paddingLeft: (((this.props.depthOffset || 0) + depth) * this.props.theme.spacing(3)),
backgroundColor: this.state.activeItem === item ? this.props.theme.palette.action.selected : undefined,
height: this.props.itemHeight || undefined,
cursor: item.disabled ? 'not-allowed' : 'pointer',
@@ -159,47 +159,47 @@ class TreeViewComponent<TData extends ITreeItem> extends React.Component<TreeVie
const searchTermLength = searchTerm && searchTerm.length || 0;
const handleClickCreator = (isIcon: boolean) => (event: React.SyntheticEvent) => {
- if (item.disabled) return;
- event.preventDefault();
- event.stopPropagation();
- if (isFolder && (this.props.autoExpandFolder || isIcon)) {
- this.props.onFolderClick ? this.props.onFolderClick(item) : this.onFolderClick(item);
- } else {
- this.props.onItemClick ? this.props.onItemClick(item) : this.onItemClick(item);
- }
- };
+ if (item.disabled) return;
+ event.preventDefault();
+ event.stopPropagation();
+ if (isFolder && (this.props.autoExpandFolder || isIcon)) {
+ this.props.onFolderClick ? this.props.onFolderClick(item) : this.onFolderClick(item);
+ } else {
+ this.props.onItemClick ? this.props.onItemClick(item) : this.onItemClick(item);
+ }
+ };
return ((searchTerm && (matchIndex > -1 || expanded) || !searchTerm)
? (
- <ListItem key={ `tree-list-${ this.itemIndex++ }` } style={ styles.item } onClick={ handleClickCreator(false) } button >
+ <ListItem key={`tree-list-${this.itemIndex++}`} style={styles.item} onClick={handleClickCreator(false)} button >
{ // display the left icon
- (this.props.useFolderIcons && <ListItemIcon>{ isFolder ? <FolderIcon /> : <FileIcon /> }</ListItemIcon>) ||
- (item.icon && (<ListItemIcon><item.icon /></ListItemIcon>)) }
+ (this.props.useFolderIcons && <ListItemIcon>{isFolder ? <FolderIcon /> : <FileIcon />}</ListItemIcon>) ||
+ (item.icon && (<ListItemIcon><item.icon /></ListItemIcon>))}
+
-
{ // highlight search result
matchIndex > -1
- ? (<span>
- { text.substring(0, matchIndex) }
- <span
- style={ {
- display: 'inline-block',
- backgroundColor: 'rgba(255,235,59,0.5)',
- padding: '3px',
- } }
- >
- { text.substring(matchIndex, matchIndex + searchTermLength) }
- </span>
- { text.substring(matchIndex + searchTermLength) }
- </span>)
- : (<ListItemText primary={ text } />)
+ ? (<span>
+ {text.substring(0, matchIndex)}
+ <span
+ style={{
+ display: 'inline-block',
+ backgroundColor: 'rgba(255,235,59,0.5)',
+ padding: '3px',
+ }}
+ >
+ {text.substring(matchIndex, matchIndex + searchTermLength)}
+ </span>
+ {text.substring(matchIndex + searchTermLength)}
+ </span>)
+ : (<ListItemText primary={text} />)
}
{ // display the right icon, depending on the state
- !isFolder ? null : expanded ? (<OpenIcon onClick={ handleClickCreator(true) } />) : (<CloseIcon onClick={ handleClickCreator(true) } />) }
+ !isFolder ? null : expanded ? (<OpenIcon onClick={handleClickCreator(true)} />) : (<CloseIcon onClick={handleClickCreator(true)} />)}
</ListItem>
- )
+ )
: null
);
}
@@ -264,5 +264,5 @@ class TreeViewComponent<TData extends ITreeItem> extends React.Component<TreeVie
export type TreeViewCtorType<TData extends ITreeItem = ITreeItem> = new () => React.Component<Omit<TreeViewComponentProps<TData>, 'theme'>>;
-export const TreeView = withTheme()(TreeViewComponent);
+export const TreeView = withTheme(TreeViewComponent);
export default TreeView; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
index e3adaeed7..00d43d99c 100644
--- a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
+++ b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
@@ -1,20 +1,20 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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. See the License for the specific language governing permissions and limitations under
- * the License.
- * ============LICENSE_END==========================================================================
- */
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
import * as React from 'react';
import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles';
@@ -29,7 +29,9 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import ListItemLink from '../components/material-ui/listItemLink';
-import connect, { Connect } from '../flux/connect';
+import connect, { Connect, IDispatcher } from '../flux/connect';
+import { MenuAction } from '../actions/menuAction';
+import * as classNames from 'classnames';
const drawerWidth = 240;
@@ -38,41 +40,101 @@ const styles = (theme: Theme) => createStyles({
position: 'relative',
width: drawerWidth,
},
- toolbar: theme.mixins.toolbar
+ toolbar: theme.mixins.toolbar,
+ drawerOpen: {
+ width: drawerWidth,
+ transition: theme.transitions.create('width', {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ },
+ drawerClose: {
+ transition: theme.transitions.create('width', {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.leavingScreen,
+ }),
+ overflowX: 'hidden',
+ width: theme.spacing(7) + 1,
+ [theme.breakpoints.up('sm')]: {
+ width: theme.spacing(9) + 1,
+ },
+ }
});
-export const NavigationMenu = withStyles(styles)(connect()(({ classes, state }: WithStyles<typeof styles> & Connect) => {
+const tabletWidthBreakpoint = 768;
+
+export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, dispatch }: WithStyles<typeof styles> & Connect & Connect) => {
const { user } = state.framework.authenticationState
+ const isOpen = state.framework.applicationState.isMenuOpen
+ const closedByUser = state.framework.applicationState.isMenuClosedByUser
+
+ const [responsive, setResponsive] = React.useState(false);
+
+ React.useEffect(() => {
+
+ function handleResize() {
+ if (user && user.isValid) {
+ if (window.innerWidth < tabletWidthBreakpoint && !responsive) {
+ setResponsive(true);
+ if (!closedByUser) {
+ console.log("responsive menu collapsed")
+ dispatch(new MenuAction(false));
+ }
+
+ } else if (window.innerWidth > tabletWidthBreakpoint && responsive) {
+ setResponsive(false);
+ if (!closedByUser) {
+ console.log("responsive menu restored")
+ dispatch(new MenuAction(true));
+ }
+
+ }
+ }
+ }
+ window.addEventListener("resize", handleResize);
+
+
+ return () => {
+ window.removeEventListener("resize", handleResize);
+ }
+ })
+
return (
<Drawer
variant="permanent"
+ className={
+ classNames({
+ [classes.drawerOpen]: isOpen,
+ [classes.drawerClose]: !isOpen
+ })
+ }
classes={{
paper: classes.drawerPaper,
}}
>
{user && user.isValid && <>
<div className={classes.toolbar} />
- { /* https://fiffty.github.io/react-treeview-mui/ */}
- <List component="nav">
+ { /* https://fiffty.github.io/react-treeview-mui/ */}
+ <List component="nav">
<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];
- return reg && (
- <ListItemLink
- key={reg.name}
- to={reg.path || `/${reg.name}`}
- primary={reg.menuEntry || reg.name}
- secondary={reg.subMenuEntry}
- icon={reg.icon && <FontAwesomeIcon icon={reg.icon} /> || null} />
- ) || null;
- }) || null
- }
- <Divider />
- <ListItemLink to="/about" primary="About" icon={<FontAwesomeIcon icon={faAddressBook} />} />
+ {
+ state.framework.applicationRegistraion && Object.keys(state.framework.applicationRegistraion).map(key => {
+ const reg = state.framework.applicationRegistraion[key];
+ return reg && (
+ <ListItemLink
+ key={reg.name}
+ to={reg.path || `/${reg.name}`}
+ primary={reg.menuEntry || reg.name}
+ secondary={reg.subMenuEntry}
+ icon={reg.icon && <FontAwesomeIcon icon={reg.icon} /> || null} />
+ ) || null;
+ }) || null
+ }
+ <Divider />
+ <ListItemLink to="/about" primary="About" icon={<FontAwesomeIcon icon={faAddressBook} />} />
</List>
- </> || null
+ </> || null
}
</Drawer>)
}));
diff --git a/sdnr/wt/odlux/framework/src/components/titleBar.tsx b/sdnr/wt/odlux/framework/src/components/titleBar.tsx
index 81759d628..7168ff496 100644
--- a/sdnr/wt/odlux/framework/src/components/titleBar.tsx
+++ b/sdnr/wt/odlux/framework/src/components/titleBar.tsx
@@ -1,20 +1,20 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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. See the License for the specific language governing permissions and limitations under
- * the License.
- * ============LICENSE_END==========================================================================
- */
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
import * as React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
@@ -24,18 +24,23 @@ import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
+import Block from '@material-ui/icons/Block';
+import Adjust from '@material-ui/icons/Adjust';
import MenuIcon from '@material-ui/icons/Menu';
import AccountCircle from '@material-ui/icons/AccountCircle';
import MenuItem from '@material-ui/core/MenuItem';
import Menu from '@material-ui/core/Menu';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faBan } from '@fortawesome/free-solid-svg-icons';
+import { faDotCircle } from '@fortawesome/free-solid-svg-icons';
import { UpdateAuthentication } from '../actions/authentication';
import { ReplaceAction } from '../actions/navigationActions';
import connect, { Connect, IDispatcher } from '../flux/connect';
import Logo from './logo';
+import { MenuAction, MenuClosedByUser } from '../actions/menuAction';
const styles = (theme: Theme) => createStyles({
appBar: {
@@ -51,6 +56,15 @@ const styles = (theme: Theme) => createStyles({
icon: {
marginLeft: 16,
marginRight: 8
+ },
+ connected: {
+ color: "green"
+ },
+ notConnected: {
+ color: "red"
+ },
+ notificationInfo: {
+ marginLeft: 5
}
});
@@ -59,6 +73,10 @@ const mapDispatch = (dispatcher: IDispatcher) => {
logout: () => {
dispatcher.dispatch(new UpdateAuthentication(null));
dispatcher.dispatch(new ReplaceAction("/login"));
+ },
+ toggleMainMenu: (value: boolean, value2: boolean) => {
+ dispatcher.dispatch(new MenuAction(value));
+ dispatcher.dispatch(new MenuClosedByUser(value2))
}
}
};
@@ -69,7 +87,6 @@ class TitleBarComponent extends React.Component<TitleBarProps, { anchorEl: HTMLE
constructor(props: TitleBarProps) {
super(props);
-
this.state = {
anchorEl: null
}
@@ -78,64 +95,103 @@ class TitleBarComponent extends React.Component<TitleBarProps, { anchorEl: HTMLE
render(): JSX.Element {
const { classes, state, history, location } = this.props;
const open = !!this.state.anchorEl;
+ let toolbarElements: Array<JSX.Element>;
+ toolbarElements = [];
+
+ // create notificationInfo element
+ const notificationInfo = state.framework.applicationState.isWebsocketAvailable != undefined ?
+ (state.framework.applicationState.isWebsocketAvailable ?
+ <Typography variant="body1" className={classes.notificationInfo}>Notifications <FontAwesomeIcon className={classes.connected} icon={faDotCircle} /> |</Typography> : <Typography variant="body1" className={classes.notificationInfo}>Notifications <FontAwesomeIcon className={classes.notConnected} icon={faBan} /> |</Typography>)
+ : <Typography variant="body1" className={classes.notificationInfo}>Notifications N/A |</Typography>;
+
+
+ // add notificationInfo element before help
+ if (state.framework.applicationRegistraion) {
+ let isNotificationInfoAdded = false;
+ Object.keys(state.framework.applicationRegistraion).map(key => {
+ const reg = state.framework.applicationRegistraion[key];
+ if (reg && reg.statusBarElement) {
+ if (key === "help") {
+ isNotificationInfoAdded = true;
+ toolbarElements.push(notificationInfo);
+ }
+ toolbarElements.push(<reg.statusBarElement key={key} />);
+ }
+ });
+
+ // add notificationInfo in case help wasn't found
+ if (!isNotificationInfoAdded) {
+ toolbarElements.push(notificationInfo);
+ }
+ }
return (
- <AppBar position="absolute" className={ classes.appBar }>
+ <AppBar position="absolute" className={classes.appBar}>
<Toolbar>
- <IconButton className={ classes.menuButton } color="inherit" aria-label="Menu">
+ <IconButton className={classes.menuButton} color="inherit" aria-label="Menu" onClick={this.toggleMainMenu}>
<MenuIcon />
</IconButton>
<Logo />
- <Typography variant="title" color="inherit" >
- { state.framework.applicationState.icon
- ? (<FontAwesomeIcon className={ classes.icon } icon={ state.framework.applicationState.icon } />)
- : null }
- { state.framework.applicationState.title }
+ <Typography variant="h6" color="inherit" >
+ {state.framework.applicationState.icon
+ ? (<FontAwesomeIcon className={classes.icon} icon={state.framework.applicationState.icon} />)
+ : null}
+ {state.framework.applicationState.title}
</Typography>
<div className={classes.grow}></div>
- { state.framework.applicationRegistraion && Object.keys(state.framework.applicationRegistraion).map(key => {
- const reg = state.framework.applicationRegistraion[key];
- return reg && reg.statusBarElement && <reg.statusBarElement key={key} /> || null
- })}
-
- { state.framework.authenticationState.user
+ {
+ // render toolbar
+ toolbarElements.map((item) => {
+ return item
+ })
+ }
+
+ {state.framework.authenticationState.user
? (<div>
- <Button
- aria-owns={ open ? 'menu-appbar' : undefined }
+ <Button aria-label="current user menu button"
+ aria-owns={open ? 'menu-appbar' : undefined}
aria-haspopup="true"
- onClick={ this.openMenu }
+ onClick={this.openMenu}
color="inherit"
>
<AccountCircle />
- { state.framework.authenticationState.user.user }
+ {state.framework.authenticationState.user.user}
</Button>
<Menu
id="menu-appbar"
- anchorEl={ this.state.anchorEl }
- anchorOrigin={ {
+ anchorEl={this.state.anchorEl}
+ anchorOrigin={{
vertical: 'top',
horizontal: 'right',
- } }
- transformOrigin={ {
+ }}
+ transformOrigin={{
vertical: 'top',
horizontal: 'right',
- } }
- open={ open }
- onClose={ this.closeMenu }
+ }}
+ open={open}
+ onClose={this.closeMenu}
>
- <MenuItem onClick={ this.closeMenu }>Profile</MenuItem>
- <MenuItem onClick={ () => {
+ {/* <MenuItem onClick={ this.closeMenu }>Profile</MenuItem> */}
+ <MenuItem onClick={() => {
this.props.logout();
this.closeMenu();
- } }>Logout</MenuItem>
+ }}>Logout</MenuItem>
</Menu>
</div>)
- : (<Button onClick={ () => { history.push('/login') } } color="inherit" disabled={ location.pathname == "/login" }>Login</Button>) }
+ : (<Button onClick={() => { history.push('/login') }} color="inherit" disabled={location.pathname == "/login"}>Login</Button>)}
</Toolbar>
</AppBar>
);
};
+ private toggleMainMenu = (event: React.MouseEvent<HTMLElement>) => {
+ console.log(this.props);
+ if (this.props.state.framework.authenticationState.user && this.props.state.framework.authenticationState.user.isValid) {
+ const isMainMenuOpen = this.props.state.framework.applicationState.isMenuOpen
+ const isClosedByUser = this.props.state.framework.applicationState.isMenuClosedByUser
+ this.props.toggleMainMenu(!isMainMenuOpen, !isClosedByUser);
+ }
+ }
private openMenu = (event: React.MouseEvent<HTMLElement>) => {
this.setState({ anchorEl: event.currentTarget });
diff --git a/sdnr/wt/odlux/framework/src/design/default.ts b/sdnr/wt/odlux/framework/src/design/default.ts
index 62e28e5c3..542c436f6 100644
--- a/sdnr/wt/odlux/framework/src/design/default.ts
+++ b/sdnr/wt/odlux/framework/src/design/default.ts
@@ -55,7 +55,26 @@ const theme = createMuiTheme({
dark: "#07819B",
contrastText: "#ffffff"
},
- }
+ },
+ overrides: { //temp fix for labels turning white after material new version (palette primary color)
+ MuiFormLabel: {
+ root: {
+ "&$focused": {
+ color: "rgba(143,143,143,1)"
+ }
+ },
+
+ focused: {}
+ },
+ MuiInput: {
+ underline: {
+
+ "&:after": {
+ borderBottom: "2px solid #444444"
+ }
+ }
+ }
+ },
});
export default theme; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/flux/action.ts b/sdnr/wt/odlux/framework/src/flux/action.ts
index 8a90f24b2..76890257e 100644
--- a/sdnr/wt/odlux/framework/src/flux/action.ts
+++ b/sdnr/wt/odlux/framework/src/flux/action.ts
@@ -1,4 +1,22 @@
/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+/**
* Represents an action in the odlux flux architecture.
*/
export abstract class Action { }
diff --git a/sdnr/wt/odlux/framework/src/flux/connect.ts b/sdnr/wt/odlux/framework/src/flux/connect.ts
index fca7df6fa..f54e4e0f0 100644
--- a/sdnr/wt/odlux/framework/src/flux/connect.ts
+++ b/sdnr/wt/odlux/framework/src/flux/connect.ts
@@ -38,19 +38,15 @@ interface IDispatchProps {
dispatch: Dispatch;
}
-type FuncInfer<T> = {
- ([...args]: any): T;
-};
-
-type FunctionResult<T> = T extends FuncInfer<infer U> ? U : never;
+type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type ComponentDecoratorInfer<TMergedProps> = {
- <TProps>(wrappedComponent: React.ComponentType<TProps & TMergedProps>): React.ComponentClass<TProps>;
+ <TProps>(wrappedComponent: React.ComponentType<TProps & TMergedProps>): React.ComponentClass<Omit<TProps & TMergedProps, keyof TMergedProps>>;
};
-export type Connect<TMapProps = undefined, TMapDispatch = undefined> =
- (TMapProps extends undefined ? IApplicationStoreProps : FunctionResult<TMapProps>) &
- (TMapDispatch extends undefined ? IDispatchProps : FunctionResult<TMapDispatch>);
+export type Connect<TMapProps extends ((...args: any) => any) | undefined = undefined, TMapDispatch extends ((...args: any) => any) | undefined = undefined> =
+ (TMapProps extends ((...args: any) => any) ? ReturnType<TMapProps> : IApplicationStoreProps) &
+ (TMapDispatch extends ((...args: any) => any) ? ReturnType<TMapDispatch> : IDispatchProps);
export function connect(): ComponentDecoratorInfer<IApplicationStoreProps & IDispatchProps>;
@@ -73,9 +69,9 @@ export function connect<TDispatchProps>(
export function connect<TProps, TStateProps, TDispatchProps>(
mapStateToProps?: ((state: IApplicationStoreState) => TStateProps),
mapDispatchToProps?: ((dispatcher: IDispatcher) => TDispatchProps)
-) :
+):
((WrappedComponent: React.ComponentType<TProps & (IApplicationStoreProps | TStateProps) & IDispatchProps>) => React.ComponentType<TProps>) {
-
+
const injectApplicationStore = (WrappedComponent: React.ComponentType<TProps & (IApplicationStoreProps | TStateProps) & IDispatchProps>): React.ComponentType<TProps> => {
class StoreAdapter extends React.Component<TProps, {}> {
@@ -83,7 +79,7 @@ export function connect<TProps, TStateProps, TDispatchProps>(
context: IApplicationStoreContext;
render(): JSX.Element {
-
+
if (isWrappedComponentIsVersion1(WrappedComponent)) {
const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, dispatch: this.store.dispatch.bind(this.store) });
return element;
@@ -91,10 +87,10 @@ export function connect<TProps, TStateProps, TDispatchProps>(
const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), dispatch: this.store.dispatch.bind(this.store) });
return element;
} else if (mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion3(WrappedComponent)) {
- const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store)}) as any) });
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) });
return element;
} else if (!mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion4(WrappedComponent)) {
- const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store)}) as any) });
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) });
return element;
}
throw new Error("Invalid arguments in connect.");
@@ -158,7 +154,7 @@ export class ApplicationStoreProvider extends React.Component<ApplicationStorePr
}
render(): JSX.Element {
- return React.Children.only(this.props.children);
+ return React.Children.only(this.props.children) as any; //type error, fix when possible
}
}
diff --git a/sdnr/wt/odlux/framework/src/flux/middleware.ts b/sdnr/wt/odlux/framework/src/flux/middleware.ts
index 6179f3bcf..de6505c4f 100644
--- a/sdnr/wt/odlux/framework/src/flux/middleware.ts
+++ b/sdnr/wt/odlux/framework/src/flux/middleware.ts
@@ -35,7 +35,7 @@ export type ActionHandlerMapObject<S extends { [key: string]: any }, A extends A
}
export const combineActionHandler = <TState extends { [key: string]: any }, TAction extends Action = Action>(actionHandlers: ActionHandlerMapObject<TState, TAction>) : IActionHandler<TState, TAction> => {
- const finalActionHandlers: ActionHandlerMapObject<TState> = {} as ActionHandlerMapObject<TState>;
+ const finalActionHandlers = {} as { [key: string]: any }; // https://github.com/microsoft/TypeScript/issues/31808
Object.keys(actionHandlers).forEach(actionHandlerKey => {
const handler = actionHandlers[actionHandlerKey];
if (typeof handler === 'function') {
@@ -55,7 +55,7 @@ export const combineActionHandler = <TState extends { [key: string]: any }, TAct
return function combination<TAction extends Action>(state: TState = ({} as TState), action: TAction) {
let hasChanged = false;
- const nextState : TState = {} as TState;
+ const nextState = {} as { [key: string]: any }; // https://github.com/microsoft/TypeScript/issues/31808
Object.keys(finalActionHandlers).forEach(key => {
const actionHandler = finalActionHandlers[key];
const previousState = state[key];
@@ -76,7 +76,7 @@ export const chainMiddleware = <TStoreState>(...middlewares: Middleware<TStoreSt
const middlewareAPI = {
getState() { return store.state },
dispatch: <TAction extends Action>(action: TAction) => store.dispatch(action) // we want to use the combinded dispatch
- // we should NOT use the flux dispatcher here, since the action would affect ALL stores
+ // we should NOT use the flux dispatcher here, since the action would affect ALL stores
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
return compose(...chain)(store.dispatch) as Dispatch;
diff --git a/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts b/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts
index 75ea92e43..a93f96a82 100644
--- a/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts
+++ b/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts
@@ -20,22 +20,25 @@ import { SetTitleAction } from '../actions/titleActions';
import { AddSnackbarNotification, RemoveSnackbarNotification } from '../actions/snackbarActions';
import { AddErrorInfoAction, RemoveErrorInfoAction, ClearErrorInfoAction } from '../actions/errorActions';
-
+import { MenuAction, MenuClosedByUser } from '../actions/menuAction'
import { IconType } from '../models/iconDefinition';
import { ErrorInfo } from '../models/errorInfo';
import { SnackbarItem } from '../models/snackbarItem';
+import { SetWebsocketAction } from '../actions/websocketAction';
export interface IApplicationState {
title: string;
appId?: string;
icon?: IconType;
-
+ isMenuOpen: boolean;
+ isMenuClosedByUser: boolean;
errors: ErrorInfo[];
snackBars: SnackbarItem[];
+ isWebsocketAvailable: boolean | undefined;
}
-const applicationStateInit: IApplicationState = { title: "Loading ...", errors: [], snackBars:[] };
+const applicationStateInit: IApplicationState = { title: "Loading ...", errors: [], snackBars: [], isMenuOpen: true, isMenuClosedByUser: false, isWebsocketAvailable: undefined };
export const applicationStateHandler: IActionHandler<IApplicationState> = (state = applicationStateInit, action) => {
if (action instanceof SetTitleAction) {
@@ -84,6 +87,22 @@ export const applicationStateHandler: IActionHandler<IApplicationState> = (state
...state,
snackBars: state.snackBars.filter(s => s.key !== action.key)
};
+ } else if (action instanceof MenuAction) {
+ state = {
+ ...state,
+ isMenuOpen: action.isOpen
+ }
+ } else if (action instanceof MenuClosedByUser) {
+ state = {
+ ...state,
+ isMenuClosedByUser: action.isClosed
+ }
+ }
+ else if (action instanceof SetWebsocketAction) {
+ state = {
+ ...state,
+ isWebsocketAvailable: action.isConnected
+ }
}
return state;
};
diff --git a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
index c0f2b4215..82b228dc0 100644
--- a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
+++ b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
@@ -21,6 +21,7 @@ import { UpdateAuthentication } from '../actions/authentication';
import { User } from '../models/authentication';
import { onLogin, onLogout } from '../services/applicationApi';
+import { startWebsocketSession, endWebsocketSession } from '../services/notificationService';
export interface IAuthenticationState {
user?: User;
@@ -28,6 +29,10 @@ export interface IAuthenticationState {
const initialToken = localStorage.getItem("userToken");
+if (initialToken !== null) {
+ startWebsocketSession();
+}
+
const authenticationStateInit: IAuthenticationState = {
user: initialToken && User.fromString(initialToken) || undefined
};
@@ -38,9 +43,11 @@ export const authenticationStateHandler: IActionHandler<IAuthenticationState> =
const user = action.bearerToken && new User(action.bearerToken) || undefined;
if (user) {
localStorage.setItem("userToken", user.toString());
+ startWebsocketSession();
onLogin();
} else {
localStorage.removeItem("userToken");
+ endWebsocketSession();
onLogout();
}
diff --git a/sdnr/wt/odlux/framework/src/models/elasticSearch.ts b/sdnr/wt/odlux/framework/src/models/elasticSearch.ts
index 62a138524..12cfd7d28 100644
--- a/sdnr/wt/odlux/framework/src/models/elasticSearch.ts
+++ b/sdnr/wt/odlux/framework/src/models/elasticSearch.ts
@@ -1,34 +1,29 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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. See the License for the specific language governing permissions and limitations under
- * the License.
- * ============LICENSE_END==========================================================================
- */
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
export type Result<TSource extends {}> = {
- aggregations: TSource;
- took: number;
- timed_out: boolean;
- _shards: {
- total: number;
- successful: number;
- failed: number;
- };
- hits: {
- total: number;
- max_score: number;
- hits?: (HitEntry<TSource>)[] | null;
- };
+ output: {
+ pagination?: {
+ size: number,
+ page: number,
+ total: number
+ },
+ data: TSource[];
+ }
}
export type HitEntry<TSource extends {}> = {
diff --git a/sdnr/wt/odlux/framework/src/models/errorInfo.ts b/sdnr/wt/odlux/framework/src/models/errorInfo.ts
index 9081f20f1..21217a108 100644
--- a/sdnr/wt/odlux/framework/src/models/errorInfo.ts
+++ b/sdnr/wt/odlux/framework/src/models/errorInfo.ts
@@ -16,6 +16,7 @@
* ============LICENSE_END==========================================================================
*/
export type ErrorInfo = {
+ title?: string,
error?: Error | null,
url?: string,
line?: number,
diff --git a/sdnr/wt/odlux/framework/src/models/restService.ts b/sdnr/wt/odlux/framework/src/models/restService.ts
index 053c29b8e..03f580b01 100644
--- a/sdnr/wt/odlux/framework/src/models/restService.ts
+++ b/sdnr/wt/odlux/framework/src/models/restService.ts
@@ -1,4 +1,22 @@
/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+/**
* The PlainObject type is a JavaScript object containing zero or more key-value pairs.
*/
export interface PlainObject<T = any> {
diff --git a/sdnr/wt/odlux/framework/src/services/forceLogoutService.ts b/sdnr/wt/odlux/framework/src/services/forceLogoutService.ts
new file mode 100644
index 000000000..ce4faab6b
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/services/forceLogoutService.ts
@@ -0,0 +1,52 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import { ApplicationStore } from "../store/applicationStore";
+import { UpdateAuthentication } from "../actions/authentication";
+import { ReplaceAction } from "../actions/navigationActions";
+
+const maxMinutesTillLogout = 15;
+let applicationStore: ApplicationStore | null;
+let tickTimer = 15;
+
+
+export const startForceLogoutService = (store: ApplicationStore) => {
+ applicationStore = store;
+ createForceLogoutInterval();
+};
+
+const createForceLogoutInterval = () => {
+ console.log("logout timer running...")
+
+ return setInterval(function () {
+ if (applicationStore && applicationStore.state.framework.authenticationState.user) {
+ tickTimer--;
+
+ if (tickTimer === 0) {
+ console.log("got logged out by timer")
+ if (applicationStore) {
+ applicationStore.dispatch(new UpdateAuthentication(null));
+ applicationStore.dispatch(new ReplaceAction("/login"));
+ }
+ }
+ }
+
+ }, 1 * 60000)
+}
+
+document.addEventListener("mousemove", function () { tickTimer = maxMinutesTillLogout; }, false) \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/services/notificationService.ts b/sdnr/wt/odlux/framework/src/services/notificationService.ts
index b12c19985..85d3f716b 100644
--- a/sdnr/wt/odlux/framework/src/services/notificationService.ts
+++ b/sdnr/wt/odlux/framework/src/services/notificationService.ts
@@ -1,24 +1,31 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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. See the License for the specific language governing permissions and limitations under
- * the License.
- * ============LICENSE_END==========================================================================
- */
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
import * as X2JS from 'x2js';
+import { ApplicationStore } from '../store/applicationStore';
+import { SetWebsocketAction } from '../actions/websocketAction';
const socketUrl = [location.protocol === 'https:' ? 'wss://' : 'ws://', 'admin', ':', 'admin', '@', location.hostname, ':', location.port, '/websocket'].join('');
-const subscriptions: { [scope: string]: SubscriptionCallback[] } = { };
+const subscriptions: { [scope: string]: SubscriptionCallback[] } = {};
+let socketReady: Promise<WebSocket>;
+let userLoggedOut = false;
+let wasWebsocketConnectionEstablished: undefined | boolean;
+let applicationStore: ApplicationStore | null;
+
export interface IFormatedMessage {
notifType: string | null;
@@ -27,7 +34,7 @@ export interface IFormatedMessage {
export type SubscriptionCallback<TMessage extends IFormatedMessage = IFormatedMessage> = (msg: TMessage) => void;
-function formatData(event: MessageEvent) : IFormatedMessage | undefined {
+function formatData(event: MessageEvent): IFormatedMessage | undefined {
var x2js = new X2JS();
var jsonObj: { [key: string]: IFormatedMessage } = x2js.xml2js(event.data);
@@ -35,7 +42,7 @@ function formatData(event: MessageEvent) : IFormatedMessage | undefined {
const notifType = Object.keys(jsonObj)[0];
const formated = jsonObj[notifType];
- formated.notifType = notifType ;
+ formated.notifType = notifType;
formated.time = new Date().toISOString();
return formated;
}
@@ -43,42 +50,31 @@ function formatData(event: MessageEvent) : IFormatedMessage | undefined {
}
-export function subscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): Promise<boolean> {
- return socketReady.then((notificationSocket) => {
- const scopes = scope instanceof Array ? scope : [scope];
+export function subscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): boolean {
+ const scopes = scope instanceof Array ? scope : [scope];
- // send all new scopes to subscribe
- const newScopesToSubscribe: string[] = scopes.reduce((acc: string[], cur: string) => {
- const currentCallbacks = subscriptions[cur];
- if (currentCallbacks) {
- if (!currentCallbacks.some(c => c === callback)) {
- currentCallbacks.push(callback);
- }
- } else {
- subscriptions[cur] = [callback];
- acc.push(cur);
+ // send all new scopes to subscribe
+ const newScopesToSubscribe: string[] = scopes.reduce((acc: string[], cur: string) => {
+ const currentCallbacks = subscriptions[cur];
+ if (currentCallbacks) {
+ if (!currentCallbacks.some(c => c === callback)) {
+ currentCallbacks.push(callback);
}
- return acc;
- }, []);
-
- if (newScopesToSubscribe.length === 0) {
- return true;
+ } else {
+ subscriptions[cur] = [callback];
+ acc.push(cur);
}
+ return acc;
+ }, []);
- // send a subscription to all active scopes
- const scopesToSubscribe = Object.keys(subscriptions);
- if (notificationSocket.readyState === notificationSocket.OPEN) {
- const data = {
- 'data': 'scopes',
- 'scopes': scopesToSubscribe
- };
- notificationSocket.send(JSON.stringify(data));
- return true;
- }
- return false;
- });
+ if (newScopesToSubscribe.length === 0) {
+ return true;
+ }
+
+ return true;
}
+
export function unsubscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): Promise<boolean> {
return socketReady.then((notificationSocket) => {
const scopes = scope instanceof Array ? scope : [scope];
@@ -107,6 +103,10 @@ export function unsubscribe<TMessage extends IFormatedMessage = IFormatedMessage
});
}
+export const startNotificationService = (store: ApplicationStore) => {
+ applicationStore = store;
+}
+
const connect = (): Promise<WebSocket> => {
return new Promise((resolve, reject) => {
const notificationSocket = new WebSocket(socketUrl);
@@ -132,22 +132,58 @@ const connect = (): Promise<WebSocket> => {
};
notificationSocket.onerror = function (error) {
- console.log("Socket error: " + error);
+ console.log("Socket error:");
+ console.log(error);
reject("Socket error: " + error);
+ if (applicationStore) {
+ applicationStore.dispatch(new SetWebsocketAction(false));
+ }
};
notificationSocket.onopen = function (event) {
+ if (applicationStore) {
+ applicationStore.dispatch(new SetWebsocketAction(true));
+ }
console.log("Socket connection opened.");
resolve(notificationSocket);
+
+ // send a subscription to all active scopes
+ const scopesToSubscribe = Object.keys(subscriptions);
+ if (notificationSocket.readyState === notificationSocket.OPEN) {
+ const data = {
+ 'data': 'scopes',
+ 'scopes': scopesToSubscribe
+ };
+ notificationSocket.send(JSON.stringify(data));
+ };
};
notificationSocket.onclose = function (event) {
- socketReady = connect();
+ console.log("socket connection closed");
+ if (applicationStore) {
+ applicationStore.dispatch(new SetWebsocketAction(false));
+ }
+ if (!userLoggedOut) {
+ socketReady = connect();
+ }
};
});
}
-let socketReady = connect();
+
+
+
+export const startWebsocketSession = () => {
+ socketReady = connect();
+ userLoggedOut = false;
+}
+
+export const endWebsocketSession = () => {
+ socketReady.then(websocket => {
+ websocket.close();
+ userLoggedOut = true;
+ })
+}
diff --git a/sdnr/wt/odlux/framework/src/services/restService.ts b/sdnr/wt/odlux/framework/src/services/restService.ts
index c25deda84..b02d7d19f 100644
--- a/sdnr/wt/odlux/framework/src/services/restService.ts
+++ b/sdnr/wt/odlux/framework/src/services/restService.ts
@@ -30,38 +30,76 @@ export const formEncode = (params: { [key: string]: string | number }) => Object
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key].toString());
}).join('&');
-export async function requestRest<TData>(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<TData | false | null> {
+/** Sends a rest request to the given path.
+ * @returns The data, or null it there was any error
+ */
+export async function requestRest<TData>(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<TData | null> {
+ const res = await requestRestExt<TData>(path, init, authenticate, isResource);
+ if (res && res.status >= 200 && res.status < 300) {
+ return res.data;
+ }
+ return null;
+}
+
+/** Sends a rest request to the given path and reports the server state.
+ * @returns An object with the server state, a message and the data.
+ */
+export async function requestRestExt<TData>(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<{ status: number, message?: string, data: TData | null }> {
+ const result: { status: number, message?: string, data: TData | null } = {
+ status: -1,
+ data: null,
+ };
const isAbsUrl = absUrlPattern.test(path);
const uri = isAbsUrl ? path : isResource ? path.replace(/\/{2,}/i, '/') : (baseUri) + ('/' + path).replace(/\/{2,}/i, '/');
- init.headers = {
+ init = {
'method': 'GET',
- 'Content-Type': 'application/json',
- 'Accept': 'application/json',
- ...init.headers
+ ...init,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ ...init.headers
+ }
};
if (!isAbsUrl && authenticate && applicationStore) {
const { state: { framework: { authenticationState: { user } } } } = applicationStore;
// do not request if the user is not valid
if (!user || !user.isValid) {
- return null;
+ return {
+ ...result,
+ message: "User is not valid or not logged in."
+ };
}
(init.headers = {
...init.headers,
- 'Authorization': `${user.tokenType} ${user.token}`
+ 'Authorization': `${user.tokenType} ${user.token}`
//'Authorization': 'Basic YWRtaW46YWRtaW4='
});
}
- const result = await fetch(uri, init);
- if (result.status === 401 || result.status === 403) {
+ const fetchResult = await fetch(uri, init);
+ if (fetchResult.status === 401 || fetchResult.status === 403) {
applicationStore && applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${applicationStore.state.framework.navigationState.pathname}`));
- return null;
+ return {
+ ...result,
+ status: 403,
+ message: "Authentication requested by server."
+ };
}
- const contentType = result.headers.get("Content-Type") || result.headers.get("content-type");
+ const contentType = fetchResult.headers.get("Content-Type") || fetchResult.headers.get("content-type");
const isJson = contentType && contentType.toLowerCase().startsWith("application/json");
try {
- const data = result.ok && (isJson ? await result.json() : await result.text()) as TData ;
- return data;
- } catch {
- return null;
+ const data = (isJson ? await fetchResult.json() : await fetchResult.text()) as TData;
+ return {
+ ...result,
+ status: fetchResult.status,
+ message: fetchResult.statusText,
+ data: data
+ };
+ } catch (error) {
+ return {
+ ...result,
+ status: fetchResult.status,
+ message: error && error.message || String(error),
+ data: null
+ };
}
} \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts b/sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts
index 54713f1b1..c18a40b0b 100644
--- a/sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts
+++ b/sdnr/wt/odlux/framework/src/utilities/elasticSearch.ts
@@ -16,70 +16,61 @@
* ============LICENSE_END==========================================================================
*/
+import { Result } from '../models';
import { DataCallback } from '../components/material-table';
-import { Result, HitEntry } from '../models';
import { requestRest } from '../services/restService';
+import { convertPropertyNames, convertPropertyValues, replaceUpperCase, replaceHyphen } from './yangHelper';
+
type propType = string | number | null | undefined | (string | number)[];
type dataType = { [prop: string]: propType };
-export function createSearchDataHandler<TResult extends {} = dataType>(uri: (() => string) | string, additionalParameters?: {}): DataCallback<(TResult & { _id: string })>;
-export function createSearchDataHandler<TResult extends {} = dataType, TData = dataType>(uri: (() => string) | 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) | 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 fetchData: DataCallback<(TData & { _id: string })> = async (page, rowsPerPage, orderBy, order, filter) => {
- const url = `${ window.location.origin }/database/${typeof uri === "function" ? uri(): uri}/_search`;
- const from = rowsPerPage && page != null && !isNaN(+page)
- ? (+page) * rowsPerPage
- : null;
+/** Represents a fabric for the searchDataHandler used by the internal data api.
+ * @param typeName The name of the entry type to create a searchDataHandler for.
+ * @param additionalFilters Filterproperties and their values to add permanently.
+ * @returns The searchDataHandler callback to be used with the material table.
+*/
+export function createSearchDataHandler<TResult>(typeName: (() => string) | string, additionalFilters?: {} | null | undefined): DataCallback<(TResult)> {
+ const fetchData: DataCallback<(TResult)> = async (pageIndex, rowsPerPage, orderBy, order, filter) => {
+ const url = `/restconf/operations/data-provider:read-${typeof typeName === "function" ? typeName(): typeName}-list`;
+
+ filter = { ...filter, ...additionalFilters };
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 : {}
+ input: {
+ filter: filterKeys.filter(f => filter![f] != null && filter![f] !== "").map(property => ({ property, filtervalue: filter![property]})),
+ sortorder: orderBy ? [{ property: orderBy, sortorder: order === "desc" ? "descending" : "ascending" }] : [],
+ pagination: { size: rowsPerPage, page: (pageIndex != null && pageIndex > 0 && pageIndex || 0) +1 }
+ }
};
- const result = await requestRest<Result<TResult & { _id: string }>>(url, {
+ const result = await requestRest<Result<TResult>>(url, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
- mode: "no-cors", // no-cors, cors, *same-origin
+ mode: "same-origin", // 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/json",
// "Content-Type": "application/x-www-form-urlencoded",
},
- body: JSON.stringify(query), // body data type must match "Content-Type" header
+ body: JSON.stringify(convertPropertyValues(query, replaceUpperCase)), // body data type must match "Content-Type" header
});
if (result) {
- let rows: (TData & { _id: string })[] = [];
+ let rows: TResult[] = [];
- if (result && result.hits && result.hits.hits) {
- rows = result.hits.hits.map( mapResult ? mapResult : h => (
- { ...(h._source as any as TData), _id: h._id }
- )) || []
+ if (result && result.output && result.output.data) {
+ rows = result.output.data.map(obj => convertPropertyNames(obj, replaceHyphen)) || []
}
const data = {
- page: Math.min(page || 0, result.hits.total || 0 / (rowsPerPage || 1)), rowCount: result.hits.total, rows: rows
+ page: result.output.pagination && result.output.pagination.page != null && result.output.pagination.page - 1 || 0 , total: result.output.pagination && result.output.pagination.total || 0, rows: rows
};
return data;
}
- return { page: 0, rowCount: 0, rows: [] };
+ return { page: 1, total: 0, rows: [] };
};
return fetchData;
diff --git a/sdnr/wt/odlux/framework/src/utilities/yangHelper.ts b/sdnr/wt/odlux/framework/src/utilities/yangHelper.ts
new file mode 100644
index 000000000..127f3e07d
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/utilities/yangHelper.ts
@@ -0,0 +1,39 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+
+export const replaceHyphen = (name: string) => name.replace(/-([a-z])/g, (g) => (g[1].toUpperCase()));
+export const replaceUpperCase = (name: string) => name.replace(/([a-z][A-Z])/g, (g) => g[0] + '-' + g[1].toLowerCase());
+
+export const convertPropertyNames = <T extends { [prop: string]: any }>(obj: T, conv: (name: string) => string): T => {
+ return Object.keys(obj).reduce<{ [prop: string]: any }>((acc, cur) => {
+ acc[conv(cur)] = typeof obj[cur] === "object" ? convertPropertyNames(obj[cur], conv) : obj[cur];
+ return acc;
+ }, obj instanceof Array ? [] : {}) as T;
+}
+
+export const convertPropertyValues = <T extends { [prop: string]: any }>(obj: T, conv: (name: string) => string): T => {
+ return Object.keys(obj).reduce<{ [prop: string]: any }>((acc, cur) => {
+ acc[cur] = typeof obj[cur] === "object"
+ ? convertPropertyValues(obj[cur], conv)
+ : cur === "property"
+ ? conv(obj[cur])
+ : obj[cur];
+ return acc;
+ }, obj instanceof Array ? [] : {}) as T;
+} \ 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 a39516de3..b93b7ee01 100644
--- a/sdnr/wt/odlux/framework/src/views/frame.tsx
+++ b/sdnr/wt/odlux/framework/src/views/frame.tsx
@@ -1,20 +1,20 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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. See the License for the specific language governing permissions and limitations under
- * the License.
- * ============LICENSE_END==========================================================================
- */
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
import * as React from 'react';
import { HashRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
@@ -49,60 +49,70 @@ const styles = (theme: Theme) => createStyles({
display: "flex",
flexDirection: "column",
backgroundColor: theme.palette.background.default,
- padding: theme.spacing.unit * 3,
+ padding: theme.spacing(3),
minWidth: 0, // So the Typography noWrap works
},
toolbar: theme.mixins.toolbar
});
-export const Frame = withStyles(styles)(({ classes }: WithStyles<typeof styles>) => {
- const registrations = applicationService.applications;
- return (
- <SnackbarProvider maxSnack={3}>
- <Router>
- <div className={ classes.root }>
- <SnackDisplay />
- <ErrorDisplay />
- <TitleBar />
- <Menu />
- <main className={ classes.content }>
- <div className={ classes.toolbar } />
- <Switch>
- <Route exact path="/" component={ () => (
- <AppFrame title={ "Home" } icon={ faHome } >
- <Home />
- </AppFrame>
- ) } />
- <Route path="/about" component={ () => (
- <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 />
- </AppFrame>
- ) } />
- { Object.keys(registrations).map(p => {
- const application = registrations[p];
- return (<Route key={ application.name } path={ application.path || `/${ application.name }` } component={ () => (
- <AppFrame title={ application.title || (typeof application.menuEntry === 'string' && application.menuEntry) || application.name } icon={ application.icon } appId={application.name} >
- <application.rootComponent />
+type FrameProps = WithStyles<typeof styles>;
+
+class FrameComponent extends React.Component<FrameProps>{
+
+ render() {
+ const registrations = applicationService.applications;
+ const { classes } = this.props;
+ return (
+ <SnackbarProvider maxSnack={3}>
+ <Router>
+ <div className={classes.root}>
+ <SnackDisplay />
+ <ErrorDisplay />
+ <TitleBar />
+ <Menu />
+ <main className={classes.content}>
+ {
+ <div className={classes.toolbar} /> //needed for margins, don't remove!
+ }
+ <Switch>
+ <Route exact path="/" component={() => (
+ <AppFrame title={"Home"} icon={faHome} >
+ <Home />
</AppFrame>
- ) } />)
- }) }
- <Redirect to="/" />
- </Switch>
- </main>
- </div>
- </Router>
- </SnackbarProvider>
- );
-});
+ )} />
+ <Route path="/about" component={() => (
+ <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 />
+ </AppFrame>
+ )} />
+ {Object.keys(registrations).map(p => {
+ const application = registrations[p];
+ return (<Route key={application.name} path={application.path || `/${application.name}`} component={() => (
+ <AppFrame title={application.title || (typeof application.menuEntry === 'string' && application.menuEntry) || application.name} icon={application.icon} appId={application.name} >
+ <application.rootComponent />
+ </AppFrame>
+ )} />)
+ })}
+ <Redirect to="/" />
+ </Switch>
+ </main>
+ </div>
+ </Router>
+ </SnackbarProvider>
+ );
+ }
+}
+
+export const Frame = withStyles(styles)(FrameComponent);
export default Frame;
diff --git a/sdnr/wt/odlux/framework/src/views/home.tsx b/sdnr/wt/odlux/framework/src/views/home.tsx
index e20c46aa4..4a4084e88 100644
--- a/sdnr/wt/odlux/framework/src/views/home.tsx
+++ b/sdnr/wt/odlux/framework/src/views/home.tsx
@@ -15,14 +15,13 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
-
-export const Home = (props: React.Props<any>) => {
- return (
- <div>
- <h1>Welcome to ODLUX.</h1>
- </div>
- )
-}
-
+import * as React from 'react';
+
+export const Home = (props: React.Props<any>) => {
+ return (
+ <div>
+ <h1>Welcome to ODLUX.</h1>
+ </div>
+ )
+}
export default Home; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/views/login.tsx b/sdnr/wt/odlux/framework/src/views/login.tsx
index 9b69ecd7e..3f6ef6134 100644
--- a/sdnr/wt/odlux/framework/src/views/login.tsx
+++ b/sdnr/wt/odlux/framework/src/views/login.tsx
@@ -45,31 +45,31 @@ const styles = (theme: Theme) => createStyles({
layout: {
width: 'auto',
display: 'block', // Fix IE11 issue.
- marginLeft: theme.spacing.unit * 3,
- marginRight: theme.spacing.unit * 3,
- [theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
+ marginLeft: theme.spacing(3),
+ marginRight: theme.spacing(3),
+ [theme.breakpoints.up(400 + theme.spacing(3) * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
- marginTop: theme.spacing.unit * 8,
+ marginTop: theme.spacing(8),
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(2)}px ${theme.spacing(3)}px ${theme.spacing(3)}px`,
},
avatar: {
- margin: theme.spacing.unit,
+ margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE11 issue.
- marginTop: theme.spacing.unit,
+ marginTop: theme.spacing(1),
},
submit: {
- marginTop: theme.spacing.unit * 3,
+ marginTop: theme.spacing(3),
},
});
@@ -87,7 +87,7 @@ interface ILoginState {
// todo: ggf. redirect to einbauen
class LoginComponent extends React.Component<LoginProps, ILoginState> {
- constructor (props: LoginProps) {
+ constructor(props: LoginProps) {
super(props);
this.state = {
@@ -142,13 +142,13 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> {
/>
</FormControl>
<FormControlLabel
- control={<Checkbox value="remember" color="primary" />}
+ control={<Checkbox value="remember" color="secondary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
- variant="raised"
+ variant="contained"
color="primary"
disabled={this.state.busy}
className={classes.submit}