ConfigApp bugfixes
fix remove button in lists and boolean filters not working, fix delay-period not configurable
Issue-ID: CCSDK-2878
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Change-Id: I49f03840de6092f9ac4641b7c98785b5feb760b4
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts
index 1db66c0..790d251 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts
+++ b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts
@@ -49,6 +49,9 @@
export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState ) => {
+ dispatch(new UpdateDeviceDescription("", {}, []));
+ dispatch(new SetCollectingSelectionData(true));
+
const availableCapabilities = await restService.getCapabilitiesByMoutId(nodeId);
if (!availableCapabilities || availableCapabilities.length <= 0) {
@@ -70,7 +73,7 @@
parser.postProcess();
- console.log(parser.modules, parser.views)
+ dispatch(new SetCollectingSelectionData(false));
return dispatch(new UpdateDeviceDescription(nodeId, parser.modules, parser.views));
}
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts b/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts
index 8f7300c..73812a4 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts
+++ b/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts
@@ -18,4 +18,10 @@
import { ViewElement } from "../models/uiModels";
-export type BaseProps<TValue = string> = { value: ViewElement, inputValue: TValue, readOnly: boolean, disabled: boolean, onChange(newValue: TValue): void };
\ No newline at end of file
+export type BaseProps<TValue = string> = {
+ value: ViewElement,
+ inputValue: TValue,
+ readOnly: boolean,
+ disabled: boolean,
+ onChange(newValue: TValue): void
+};
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx
new file mode 100644
index 0000000..c705b98
--- /dev/null
+++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx
@@ -0,0 +1,204 @@
+/**
+ * ============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 { FormControl, InputLabel, Paper, Chip, FormHelperText, Dialog, DialogTitle, DialogContentText, DialogActions, Button, DialogContent } from "@material-ui/core";
+import { makeStyles } from '@material-ui/core/styles';
+import AddIcon from '@material-ui/icons/Add';
+
+import { ViewElement } from "../models/uiModels";
+
+import { BaseProps } from "./baseProps";
+
+type LeafListProps = BaseProps<any []> & {
+ getEditorForViewElement: (uiElement: ViewElement) => (null | React.ComponentType<BaseProps<any>>)
+};
+
+const useStyles = makeStyles((theme) => {
+ const light = theme.palette.type === 'light';
+ const bottomLineColor = light ? 'rgba(0, 0, 0, 0.42)' : 'rgba(255, 255, 255, 0.7)';
+
+ return ({
+ root: {
+ display: 'flex',
+ justifyContent: 'left',
+ verticalAlign: 'bottom',
+ flexWrap: 'wrap',
+ listStyle: 'none',
+ margin: 0,
+ padding: 0,
+ paddingTop: theme.spacing(0.5),
+ marginTop: theme.spacing(1),
+ },
+ chip: {
+ margin: theme.spacing(0.5),
+ },
+ underline: {
+ '&:after': {
+ borderBottom: `2px solid ${theme.palette.primary.main}`,
+ left: 0,
+ bottom: 0,
+ // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242
+ content: '""',
+ position: 'absolute',
+ right: 0,
+ transform: 'scaleX(0)',
+ transition: theme.transitions.create('transform', {
+ duration: theme.transitions.duration.shorter,
+ easing: theme.transitions.easing.easeOut,
+ }),
+ pointerEvents: 'none', // Transparent to the hover style.
+ },
+ '&$focused:after': {
+ transform: 'scaleX(1)',
+ },
+ '&$error:after': {
+ borderBottomColor: theme.palette.error.main,
+ transform: 'scaleX(1)', // error is always underlined in red
+ },
+ '&:before': {
+ borderBottom: `1px solid ${bottomLineColor}`,
+ left: 0,
+ bottom: 0,
+ // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242
+ content: '"\\00a0"',
+ position: 'absolute',
+ right: 0,
+ transition: theme.transitions.create('border-bottom-color', {
+ duration: theme.transitions.duration.shorter,
+ }),
+ pointerEvents: 'none', // Transparent to the hover style.
+ },
+ '&:hover:not($disabled):before': {
+ borderBottom: `2px solid ${theme.palette.text.primary}`,
+ // Reset on touch devices, it doesn't add specificity
+ '@media (hover: none)': {
+ borderBottom: `1px solid ${bottomLineColor}`,
+ },
+ },
+ '&$disabled:before': {
+ borderBottomStyle: 'dotted',
+ },
+ },
+ })
+});
+
+export const UiElementLeafList = (props: LeafListProps) => {
+ const { value: element, inputValue, onChange } = props;
+
+ const classes = useStyles();
+
+ const [open, setOpen] = React.useState(false);
+ const [editorValue, setEditorValue] = React.useState("");
+ const [editorValueIndex, setEditorValueIndex] = React.useState(-1);
+
+
+ const handleClickOpen = () => {
+ setOpen(true);
+ };
+
+ const handleClose = () => {
+ setOpen(false);
+ };
+
+ const onApplyButton = () => {
+ if (editorValue != null && editorValue != "" && editorValueIndex < 0) {
+ props.onChange([
+ ...inputValue,
+ editorValue,
+ ]);
+ } else if (editorValue != null && editorValue != "") {
+ props.onChange([
+ ...inputValue.slice(0, editorValueIndex),
+ editorValue,
+ ...inputValue.slice(editorValueIndex+1),
+ ]);
+ }
+ setOpen(false);
+ };
+
+ const onDelete = (index : number) => {
+ const newValue : any[] = [
+ ...inputValue.slice(0, index),
+ ...inputValue.slice(index+1),
+ ];
+ onChange(newValue);
+ };
+
+ const ValueEditor = props.getEditorForViewElement(props.value);
+
+ return (
+ <>
+ <FormControl style={{ width: 485, marginLeft: 20, marginRight: 20 }}>
+ <InputLabel htmlFor={`list-${element.id}`} shrink={!props.readOnly || !!(inputValue && inputValue.length)} >{element.label}</InputLabel>
+ <ul className={`${classes.root} ${classes.underline}`} id={`list-${element.id}`}>
+ { !props.readOnly ? <li>
+ <Chip
+ icon={<AddIcon />}
+ label={"Add"}
+ className={classes.chip}
+ size="small"
+ color="secondary"
+ onClick={ () => {
+ setOpen(true);
+ setEditorValue("");
+ setEditorValueIndex(-1);
+ }
+ }
+ />
+ </li> : null }
+ { inputValue.map((val, ind) => (
+ <li key={ind}>
+ <Chip
+ className={classes.chip}
+ size="small"
+ variant="outlined"
+ label={String(val)}
+ onDelete={ !props.readOnly ? () => { onDelete(ind); } : undefined }
+ onClick={ !props.readOnly ? () => {
+ setOpen(true);
+ setEditorValue(val);
+ setEditorValueIndex(ind);
+ } : undefined
+ }
+ />
+ </li>
+ ))
+ }
+ </ul>
+ {/* <FormHelperText>{ "Value is mandetory"}</FormHelperText> */}
+ </FormControl>
+ <Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
+ <DialogTitle id="form-dialog-title">{editorValueIndex < 0 ? "Add new value" : "Edit value" } </DialogTitle>
+ <DialogContent>
+ { ValueEditor && <ValueEditor
+ inputValue={ editorValue }
+ value={{ ...element, isList: false}}
+ disabled={false}
+ readOnly={props.readOnly}
+ onChange={ setEditorValue }
+ /> || null }
+ </DialogContent>
+ <DialogActions>
+ <Button onClick={ handleClose }> Cancel </Button>
+ <Button disabled={editorValue == null || editorValue === "" } onClick={ onApplyButton } color="secondary"> {editorValueIndex < 0 ? "Add" : "Apply"} </Button>
+ </DialogActions>
+ </Dialog>
+ </>
+ );
+};
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx
index 6465feb..fc3c68e 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx
+++ b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx
@@ -45,15 +45,19 @@
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import Breadcrumbs from "@material-ui/core/Breadcrumbs";
+import { Button } from '@material-ui/core';
import Link from "@material-ui/core/Link";
+import { BaseProps } from '../components/baseProps';
import { UIElementReference } from '../components/uiElementReference';
import { UiElementNumber } from '../components/uiElementNumber';
import { UiElementString } from '../components/uiElementString';
import { UiElementBoolean } from '../components/uiElementBoolean';
import { UiElementSelection } from '../components/uiElementSelection';
import { UIElementUnion } from '../components/uiElementUnion';
-import { Button } from '@material-ui/core';
+import { UiElementLeafList } from '../components/uiElementLeafList';
+
+import { useConfirm } from 'material-ui-confirm';
const styles = (theme: Theme) => createStyles({
header: {
@@ -281,68 +285,130 @@
}, {} as { [key: string]: any });
}
+ private getEditorForViewElement = (uiElement: ViewElement) : (null | React.ComponentType<BaseProps<any>>) => {
+ if (isViewElementEmpty(uiElement)) {
+ return null;
+ } else if (isViewElementSelection(uiElement)) {
+ return UiElementSelection;
+ } else if (isViewElementBoolean(uiElement)) {
+ return UiElementBoolean;
+ } else if (isViewElementString(uiElement)) {
+ return UiElementString;
+ } else if (isViewElementNumber(uiElement)) {
+ return UiElementNumber;
+ } else if (isViewElementUnion(uiElement)) {
+ return UIElementUnion;
+ } else {
+ if (process.env.NODE_ENV !== "production") {
+ console.error(`Unknown element type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`)
+ }
+ return null;
+ }
+ }
+
private renderUIElement = (uiElement: ViewElement, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => {
const isKey = (uiElement.label === keyProperty);
const canEdit = editMode && (isNew || (uiElement.config && !isKey));
-
- // do not show elements w/o any value from the backend
+
+ // do not show elements w/o any value from the backend
if (viewData[uiElement.id] == null && !editMode) {
return null;
- } else if (isViewElementEmpty(uiElement)) {
+ } else if (isViewElementEmpty(uiElement)) {
return null;
- } else if (isViewElementSelection(uiElement)) {
-
- return <UiElementSelection
- key={uiElement.id}
- inputValue={viewData[uiElement.id] || ''}
- value={uiElement}
- readOnly={!canEdit}
- disabled={editMode && !canEdit}
- onChange={(e) => { this.changeValueFor(uiElement.id, e) }}
- />
-
- } else if (isViewElementBoolean(uiElement)) {
- return <UiElementBoolean
- key={uiElement.id}
- inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]}
- value={uiElement}
- readOnly={!canEdit}
- disabled={editMode && !canEdit}
- onChange={(e) => { this.changeValueFor(uiElement.id, e) }} />
-
- } else if (isViewElementString(uiElement)) {
- return <UiElementString
- key={uiElement.id}
- inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]}
- value={uiElement}
- isKey={isKey}
- readOnly={!canEdit}
- disabled={editMode && !canEdit}
- onChange={(e) => { this.changeValueFor(uiElement.id, e) }} />
-
- } else if (isViewElementNumber(uiElement)) {
- return <UiElementNumber
- key={uiElement.id}
- value={uiElement}
- inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]}
- readOnly={!canEdit}
- disabled={editMode && !canEdit}
- onChange={(e) => { this.changeValueFor(uiElement.id, e) }} />
- } else if (isViewElementUnion(uiElement)) {
- return <UIElementUnion
- key={uiElement.id}
- isKey={false}
- value={uiElement}
- inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]}
- readOnly={!canEdit}
- disabled={editMode && !canEdit}
- onChange={(e) => { this.changeValueFor(uiElement.id, e) }} />
+ } else if (uiElement.isList) {
+ /* element is a leaf-list */
+ return <UiElementLeafList
+ key={uiElement.id}
+ inputValue={viewData[uiElement.id] == null ? [] : viewData[uiElement.id]}
+ value={uiElement}
+ readOnly={!canEdit}
+ disabled={editMode && !canEdit}
+ onChange={(e) => { this.changeValueFor(uiElement.id, e) }}
+ getEditorForViewElement = { this.getEditorForViewElement }
+ />;
} else {
- if (process.env.NODE_ENV !== "production") {
- console.error(`Unknown element type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`)
- }
- return null;
+ const Element = this.getEditorForViewElement(uiElement);
+ return Element != null
+ ? (
+ <Element
+ key={uiElement.id}
+ inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]}
+ value={uiElement}
+ readOnly={!canEdit}
+ disabled={editMode && !canEdit}
+ onChange={(e) => { this.changeValueFor(uiElement.id, e) }}
+ /> )
+ : null ;
}
+
+ // // do not show elements w/o any value from the backend
+ // if (viewData[uiElement.id] == null && !editMode) {
+ // return null;
+ // } else if (isViewElementEmpty(uiElement)) {
+ // return null;
+ // } else if (uiElement.isList) {
+ // /* element is a leaf-list */
+ // return <UiElementLeafList
+ // key={uiElement.id}
+ // inputValue={viewData[uiElement.id] || ''}
+ // value={uiElement}
+ // readOnly={!canEdit}
+ // disabled={editMode && !canEdit}
+ // onChange={(e) => { this.changeValueFor(uiElement.id, e) }}
+ // />;
+ // } else if (isViewElementSelection(uiElement)) {
+
+ // return <UiElementSelection
+ // key={uiElement.id}
+ // inputValue={viewData[uiElement.id] || ''}
+ // value={uiElement}
+ // readOnly={!canEdit}
+ // disabled={editMode && !canEdit}
+ // onChange={(e) => { this.changeValueFor(uiElement.id, e) }}
+ // />
+
+ // } else if (isViewElementBoolean(uiElement)) {
+ // return <UiElementBoolean
+ // key={uiElement.id}
+ // inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]}
+ // value={uiElement}
+ // readOnly={!canEdit}
+ // disabled={editMode && !canEdit}
+ // onChange={(e) => { this.changeValueFor(uiElement.id, e) }} />
+
+ // } else if (isViewElementString(uiElement)) {
+ // return <UiElementString
+ // key={uiElement.id}
+ // inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]}
+ // value={uiElement}
+ // isKey={isKey}
+ // readOnly={!canEdit}
+ // disabled={editMode && !canEdit}
+ // onChange={(e) => { this.changeValueFor(uiElement.id, e) }} />
+
+ // } else if (isViewElementNumber(uiElement)) {
+ // return <UiElementNumber
+ // key={uiElement.id}
+ // value={uiElement}
+ // inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]}
+ // readOnly={!canEdit}
+ // disabled={editMode && !canEdit}
+ // onChange={(e) => { this.changeValueFor(uiElement.id, e) }} />
+ // } else if (isViewElementUnion(uiElement)) {
+ // return <UIElementUnion
+ // key={uiElement.id}
+ // isKey={false}
+ // value={uiElement}
+ // inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]}
+ // readOnly={!canEdit}
+ // disabled={editMode && !canEdit}
+ // onChange={(e) => { this.changeValueFor(uiElement.id, e) }} />
+ // } else {
+ // if (process.env.NODE_ENV !== "production") {
+ // console.error(`Unknown element type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`)
+ // }
+ // return null;
+ // }
};
// private renderUIReference = (uiElement: ViewElement, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => {
@@ -399,7 +465,7 @@
Object.keys(uiElement.cases).map(caseKey => {
const caseElm = uiElement.cases[caseKey];
return (
- <MenuItem key={caseElm.id} title={caseElm.description} value={caseKey}>{caseElm.label}</MenuItem>
+ <MenuItem key={caseElm.id} value={caseKey}><Tooltip title={caseElm.description}><div style={{width:"100%"}}>{caseElm.label}</div></Tooltip></MenuItem>
);
})
}
@@ -508,31 +574,41 @@
const { classes, removeElement } = this.props;
+ const DeleteIconWithConfirmation : React.FC<{rowData: {[key:string]:any}, onReload: () => void} > = (props) => {
+ const confirm = useConfirm();
+
+ return (
+ <Tooltip title={"Remove"} >
+ <IconButton className={classes.button} onClick={async (e) => {
+ e.stopPropagation();
+ e.preventDefault();
+ confirm({title: "Do you really want to delete this element ?", description: "This action is permanent!", confirmationButtonProps: { color: "secondary" }})
+ .then(() => removeElement(`${this.props.vPath}[${props.rowData[listKeyProperty]}]`))
+ .then( props.onReload );
+ }} >
+ <RemoveIcon />
+ </IconButton>
+ </Tooltip>
+ );
+ }
+
return (
<SelectElementTable stickyHeader idProperty={listKeyProperty} rows={listData} customActionButtons={[addNewElementAction]} columns={
Object.keys(listElements).reduce<ColumnModel<{ [key: string]: any }>[]>((acc, cur) => {
const elm = listElements[cur];
if (elm.uiType !== "object" && listData.every(entry => entry[elm.label] != null)) {
if (elm.label !== listKeyProperty) {
- acc.push({ property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text });
+ acc.push(elm.uiType === "boolean" ? { property: elm.label, type: ColumnType.boolean } : { property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text });
} else {
- acc.unshift({ property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text });
+ acc.unshift(elm.uiType === "boolean" ? { property: elm.label, type: ColumnType.boolean } : { property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text });
}
}
return acc;
}, []).concat([{
property: "Actions", disableFilter: true, disableSorting: true, type: ColumnType.custom, customControl: ( ({ rowData })=> {
return (
- <Tooltip title={"Remove"} >
- <IconButton className={classes.button} onClick={(e) => {
- e.stopPropagation();
- e.preventDefault();
- removeElement(`${this.props.vPath}[${rowData[listKeyProperty]}]`)
- }} >
- <RemoveIcon />
- </IconButton>
- </Tooltip>
- )
+ <DeleteIconWithConfirmation rowData={rowData} onReload={() => this.props.vPath && this.props.reloadView(this.props.vPath) } />
+ );
})
}])
} onHandleClick={(ev, row) => {
@@ -725,7 +801,7 @@
<div className={this.props.classes.outer}>
<div className={this.props.classes.inner}>
<Loader />
- <h3>Collecting Data ...</h3>
+ <h3>Processing ...</h3>
</div>
</div>
);
diff --git a/sdnr/wt/odlux/framework/src/services/restService.ts b/sdnr/wt/odlux/framework/src/services/restService.ts
index 19ef34f..f05c7b8 100644
--- a/sdnr/wt/odlux/framework/src/services/restService.ts
+++ b/sdnr/wt/odlux/framework/src/services/restService.ts
@@ -85,7 +85,7 @@
};
}
const contentType = fetchResult.headers.get("Content-Type") || fetchResult.headers.get("content-type");
- const isJson = contentType && contentType.toLowerCase().startsWith("application/json");
+ const isJson = contentType && (contentType.toLowerCase().startsWith("application/json") || contentType.toLowerCase().startsWith("application/yang-data+json"));
try {
const data = (isJson ? await fetchResult.json() : await fetchResult.text()) as TData;
return {
diff --git a/sdnr/wt/odlux/framework/src/views/frame.tsx b/sdnr/wt/odlux/framework/src/views/frame.tsx
index 5218975..c8e24fd 100644
--- a/sdnr/wt/odlux/framework/src/views/frame.tsx
+++ b/sdnr/wt/odlux/framework/src/views/frame.tsx
@@ -21,6 +21,9 @@
import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles';
import { faHome, faAddressBook, faSignInAlt } from '@fortawesome/free-solid-svg-icons';
+import { SnackbarProvider } from 'notistack';
+import { ConfirmProvider } from 'material-ui-confirm';
+
import AppFrame from '../components/routing/appFrame';
import TitleBar from '../components/titleBar';
import Menu from '../components/navigationMenu';
@@ -33,7 +36,7 @@
import Test from '../views/test';
import applicationService from '../services/applicationManager';
-import { SnackbarProvider } from 'notistack';
+
const styles = (theme: Theme) => createStyles({
root: {
@@ -63,52 +66,54 @@
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>
- )} />
- <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 />
+ <ConfirmProvider>
+ <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>
+ </ConfirmProvider>
);
}
}
diff --git a/sdnr/wt/odlux/package.json b/sdnr/wt/odlux/package.json
index 9053e08..4eaf5fc 100644
--- a/sdnr/wt/odlux/package.json
+++ b/sdnr/wt/odlux/package.json
@@ -32,6 +32,7 @@
"jsonwebtoken": "8.3.0",
"jss": "10.0.3",
"lerna": "3.13.1",
+ "material-ui-confirm": "2.1.1",
"notistack": "0.9.6",
"prop-types": "15.7.2",
"react": "16.12.0",
diff --git a/sdnr/wt/odlux/yarn.lock b/sdnr/wt/odlux/yarn.lock
index e71ad3f..9f1065b 100644
--- a/sdnr/wt/odlux/yarn.lock
+++ b/sdnr/wt/odlux/yarn.lock
@@ -7274,6 +7274,11 @@
resolved "https://registry.yarnpkg.com/marked/-/marked-0.6.0.tgz#a18d01cfdcf8d15c3c455b71c8329e5e0f01faa1"
integrity sha512-HduzIW2xApSXKXJSpCipSxKyvMbwRRa/TwMbepmlZziKdH8548WSoDP4SxzulEKjlo8BE39l+2fwJZuRKOln6g==
+material-ui-confirm@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/material-ui-confirm/-/material-ui-confirm-2.1.1.tgz#dbc3ff66502a183966a3f0a2ebb2ff8ba22a148d"
+ integrity sha512-d671LgozdJP54buZTv+Eemo0ySYTCXF3QqfYKO7axoG/8g659G5+aD7PovYupsfSSOXOzyzpYRhjbIhE4yrPHA==
+
math-random@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"