diff options
author | Aijana Schumann <aijana.schumann@highstreet-technologies.com> | 2020-10-07 13:51:14 +0200 |
---|---|---|
committer | Aijana Schumann <aijana.schumann@highstreet-technologies.com> | 2020-10-07 13:51:14 +0200 |
commit | aee5dc5c1a62ba13c792028e9eea5886a680eb79 (patch) | |
tree | 1d821337a3f01b7b4374dae2662bb2420958b55b /sdnr/wt | |
parent | d5bea98dfb5ff7dafb9adaecea53a4d6a9987102 (diff) |
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
Diffstat (limited to 'sdnr/wt')
8 files changed, 418 insertions, 118 deletions
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts index 1db66c0d4..790d2515c 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 class UpdatOutputData extends Action { 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 @@ export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatc 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 8f7300cfb..73812a4b8 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 000000000..c705b982f --- /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 6465feb4e..fc3c68e88 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 InputLabel from "@material-ui/core/InputLabel"; 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 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp }, {} 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 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp 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 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp 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 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp <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 19ef34f91..f05c7b89f 100644 --- a/sdnr/wt/odlux/framework/src/services/restService.ts +++ b/sdnr/wt/odlux/framework/src/services/restService.ts @@ -85,7 +85,7 @@ export async function requestRestExt<TData>(path: string = '', init: RequestInit }; } 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 521897554..c8e24fdb9 100644 --- a/sdnr/wt/odlux/framework/src/views/frame.tsx +++ b/sdnr/wt/odlux/framework/src/views/frame.tsx @@ -21,6 +21,9 @@ import { HashRouter as Router, Route, Redirect, Switch } from 'react-router-dom' 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 About from '../views/about'; import Test from '../views/test';
import applicationService from '../services/applicationManager';
-import { SnackbarProvider } from 'notistack';
+
const styles = (theme: Theme) => createStyles({
root: {
@@ -63,52 +66,54 @@ class FrameComponent extends React.Component<FrameProps>{ 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>
+ )} />
+ <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>
- )} />)
- })}
- <Redirect to="/" />
- </Switch>
- </main>
- </div>
- </Router>
- </SnackbarProvider>
+ )} />
+ {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 9053e0886..4eaf5fc26 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 e71ad3f44..9f1065b28 100644 --- a/sdnr/wt/odlux/yarn.lock +++ b/sdnr/wt/odlux/yarn.lock @@ -7274,6 +7274,11 @@ marked@0.6.0: 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" |