Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port #375 to release 8 #376

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/Moryx.CommandCenter.Web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@
"@types/uuid": "^9.0.8",
"bootstrap": "4.6.2",
"bootstrap5-toggle": "^5.0.6",
"history": "^4.10.1",
"moment": "^2.30.1",
"query-string": "^8.2.0",
"react": "18.2.0",
"react-bootstrap-toggle": "^2.3.2",
"react-dom": "18.2.0",
"react-redux": "^9.1.0",
"react-router": "^5.3.4",
"react-router-dom": "^5.3.4",
"react-router": "^6.22.0",
"react-router-dom": "^6.22.0",
"react-router-redux": "^4.0.8",
"react-toastify": "^10.0.4",
"reactstrap": "^8.10.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,35 @@
*/

import * as React from "react";
import { Link, RouteComponentProps, withRouter } from "react-router-dom";
import { Collapse } from "reactstrap";
import { useNavigate } from "react-router-dom";
import MenuItemModel from "../../models/MenuItemModel";
import MenuModel from "../../models/MenuModel";
import RoutingMenuItem from "./RoutingMenuItem";
import { MenuProps } from "./TreeMenu";

class RoutingMenu extends React.Component<RouteComponentProps<{}> & MenuProps, {}> {
function RoutingMenu(props: MenuProps) {
const navigate = useNavigate();

constructor(props: RouteComponentProps<{}> & MenuProps) {
super (props);
this.state = {};
}

protected handleMenuItemClick(menuItem: MenuItemModel): void {
if (this.props.onActiveMenuItemChanged != null) {
this.props.onActiveMenuItemChanged(menuItem);
const handleMenuItemClick = (menuItem: MenuItemModel): void => {
if (props.onActiveMenuItemChanged != null) {
props.onActiveMenuItemChanged(menuItem);
}
this.props.history.push(menuItem.NavPath);
}
navigate(menuItem.NavPath);
};

protected renderMenu(menuItems: MenuItemModel[]): React.ReactNode {
return menuItems.map ((menuItem, idx) => {
const renderMenu = (menuItems: MenuItemModel[]): React.ReactNode => {
return menuItems.map((menuItem, idx) => {
return (
<RoutingMenuItem key={idx} MenuItem={menuItem} Level={0} onMenuItemClicked={(menuItem) => this.handleMenuItemClick(menuItem)} />
<RoutingMenuItem
key={idx}
MenuItem={menuItem}
Level={0}
onMenuItemClicked={(menuItem) => handleMenuItemClick(menuItem)}
/>
);
});
}
};

public render(): React.ReactNode {
return (
<div>
{this.renderMenu(this.props.Menu.MenuItems)}
</div>
);
}
return <div>{renderMenu(props.Menu.MenuItems)}</div>;
}

export default withRouter<RouteComponentProps<{}> & MenuProps, React.ComponentType<any>>(RoutingMenu);
export default RoutingMenu;
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/*
* Copyright (c) 2020, Phoenix Contact GmbH & Co. KG
* Licensed under the Apache License, Version 2.0
* Copyright (c) 2020, Phoenix Contact GmbH & Co. KG
* Licensed under the Apache License, Version 2.0
*/

import { Location, UnregisterCallback } from "history";
import * as React from "react";
import { Link, RouteComponentProps, withRouter } from "react-router-dom";
import { Link, Location, useLocation, useNavigate } from "react-router-dom";
import { ListGroupItem } from "reactstrap";
import MenuItemModel from "../../models/MenuItemModel";

Expand All @@ -19,54 +18,42 @@ interface MenuItemState {
IsOpened: boolean;
}

class RoutingMenuItem extends React.Component<RouteComponentProps<{}> & MenuItemProps, MenuItemState> {
private unregisterListenerCallback: UnregisterCallback;
function RoutingMenuItem(props: MenuItemProps) {
const location = useLocation();
const navigate = useNavigate();

constructor(props: RouteComponentProps<{}> & MenuItemProps) {
super(props);
this.state = { IsOpened: this.isOpened(this.props.location) };
const isOpened = (location: Location): boolean => {
return location.pathname.startsWith(props.MenuItem.NavPath);
};

this.unregisterListenerCallback = this.props.history.listen(this.onRouteChanged.bind(this));
this.onMenuItemClicked = this.onMenuItemClicked.bind(this);
}
const [IsOpened, setIsOpened] = React.useState<boolean>(isOpened(location));

public componentWillUnmount(): void {
this.unregisterListenerCallback();
}
React.useEffect(() => {
setIsOpened(isOpened(location));
}, [navigate]);

private isOpened(location: Location): boolean {
return location.pathname.startsWith(this.props.MenuItem.NavPath);
}

private onRouteChanged(location: Location, action: string): void {
this.setState({ IsOpened: this.isOpened(location) });
}

private handleMenuItemClick(e: React.MouseEvent<HTMLElement>): void {
const handleMenuItemClick = (e: React.MouseEvent<HTMLElement>): void => {
e.preventDefault();
setIsOpened((prevState) => !prevState);
onMenuItemClicked(props.MenuItem);
};

this.setState((prevState) => ({ IsOpened: !prevState.IsOpened }));
this.onMenuItemClicked(this.props.MenuItem);
}

private onMenuItemClicked(menuItem: MenuItemModel): void {
if (this.props.onMenuItemClicked != null) {
this.props.onMenuItemClicked(menuItem);
const onMenuItemClicked = (menuItem: MenuItemModel): void => {
if (props.onMenuItemClicked != null) {
props.onMenuItemClicked(menuItem);
}
}

public render(): React.ReactNode {
const isActive = this.props.location.pathname.includes(this.props.MenuItem.NavPath);

return (
<ListGroupItem active={isActive} className="menu-item" onClick={(e: React.MouseEvent<HTMLElement>) => this.handleMenuItemClick(e)}>
<Link to={this.props.MenuItem.NavPath}>
{this.props.MenuItem.Name}
</Link>
{this.props.MenuItem.Content}
</ListGroupItem >
);
}
};

const isActive = isOpened(location);

return (
<ListGroupItem active={isActive} className="menu-item" onClick={(e: React.MouseEvent<HTMLElement>) => handleMenuItemClick(e)}>
<Link to={props.MenuItem.NavPath}>
{props.MenuItem.Name}
</Link>
{props.MenuItem.Content}
</ListGroupItem>
);
}

export default withRouter<RouteComponentProps<{}> & MenuItemProps, React.ComponentType<any>>(RoutingMenuItem);
export default RoutingMenuItem;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import { mdiChevronDown, mdiChevronUp } from "@mdi/js";
import Icon from "@mdi/react";
import * as React from "react";
import { Link, RouteComponentProps, withRouter } from "react-router-dom";
import { Col, Collapse, Container, Row } from "reactstrap";
import MenuItemModel, { IconType } from "../../models/MenuItemModel";

Expand All @@ -22,7 +21,7 @@ interface MenuItemState {

export default class TreeMenuItem extends React.Component<MenuItemProps, MenuItemState> {
constructor(props: MenuItemProps) {
super (props);
super(props);
this.state = { IsOpened: false };

this.onMenuItemClicked = this.onMenuItemClicked.bind(this);
Expand All @@ -42,47 +41,47 @@ export default class TreeMenuItem extends React.Component<MenuItemProps, MenuIte
}

private renderSubMenuItems(): React.ReactNode {
return this.props.MenuItem.SubMenuItems.map ((menuItem, idx) =>
return this.props.MenuItem.SubMenuItems.map((menuItem, idx) =>
<TreeMenuItem key={idx}
MenuItem={menuItem}
Level={this.props.Level + 1}
onMenuItemClicked={this.onMenuItemClicked}
/>);
MenuItem={menuItem}
Level={this.props.Level + 1}
onMenuItemClicked={this.onMenuItemClicked}
/>);
}

public render(): React.ReactNode {
const hasSubItems = this.props.MenuItem.SubMenuItems.length > 0;
const iconType = this.props.MenuItem.IconType == undefined ? IconType.Icon : IconType.Image;
const defaultContent = (
<div>
{ this.props.MenuItem.Icon !== undefined && iconType === IconType.Icon &&
{this.props.MenuItem.Icon !== undefined && iconType === IconType.Icon &&
<Icon path={this.props.MenuItem.Icon} className="icon right-space" />
}
{ this.props.MenuItem.Icon !== undefined && iconType === IconType.Image &&
<img src={this.props.MenuItem.Icon} style={{marginRight: "4px"}} />
{this.props.MenuItem.Icon !== undefined && iconType === IconType.Image &&
<img src={this.props.MenuItem.Icon} style={{ marginRight: "4px" }} />
}
<Icon path={this.props.MenuItem.Icon} className="icon right-sapce" />
<span style={{wordBreak: "break-all"}}>{this.props.MenuItem.Name}</span>
<span style={{ wordBreak: "break-all" }}>{this.props.MenuItem.Name}</span>
</div>
);

return (
<div style={{paddingLeft: this.props.Level * 10 + "px", margin: "5px 0px 5px 0px"}}>
<div style={{ paddingLeft: this.props.Level * 10 + "px", margin: "5px 0px 5px 0px" }}>
<Container fluid={true} className="menu-item">
<Row>
<Col md={10}>
<div>
{ this.props.MenuItem.Content === undefined &&
{this.props.MenuItem.Content === undefined &&
defaultContent
}
{ this.props.MenuItem.Content !== undefined &&
{this.props.MenuItem.Content !== undefined &&
this.props.MenuItem.Content
}
</div>
</Col>
<Col md={2} onClick={(e: React.MouseEvent<HTMLElement>) => this.handleMenuItemClick(e)}>
{ hasSubItems &&
<Icon path={this.state.IsOpened ? mdiChevronUp : mdiChevronDown} className="icon"/>
{hasSubItems &&
<Icon path={this.state.IsOpened ? mdiChevronUp : mdiChevronDown} className="icon" />
}
</Col>
</Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class ModuleHeader extends React.Component<ModulePropModel & ModuleDispat
<Navbar className="navbar-default" expand="md">
<Nav className="navbar-left" navbar={true}>
<NavItem>
<NavLink exact={true} to={`/modules/${this.props.ModuleName}`} className="navbar-nav-link">
<NavLink end={true} to={`/modules/${this.props.ModuleName}`} className="navbar-nav-link">
<Icon path={mdiMonitor} className="icon right-space" />
Overview
</NavLink>
Expand All @@ -61,7 +61,7 @@ export class ModuleHeader extends React.Component<ModulePropModel & ModuleDispat
</NavItem>
<NavItem >
<NavLink to={`/modules/${this.props.ModuleName}/console`} className="navbar-nav-link">
<Icon path={mdiConsoleLine}className="icon right-space" />
<Icon path={mdiConsoleLine} className="icon right-space" />
Console
</NavLink>
</NavItem>
Expand Down
84 changes: 37 additions & 47 deletions src/Moryx.CommandCenter.Web/src/common/container/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

import * as React from "react";
import { connect } from "react-redux";
import { Redirect, Route, RouteComponentProps, Switch, withRouter } from "react-router-dom";
import { toast, ToastContainer } from "react-toastify";
import { Navigate, Route, Routes } from "react-router-dom";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { Container } from "reactstrap";
import DatabasesRestClient from "../../databases/api/DatabasesRestClient";
Expand Down Expand Up @@ -71,56 +71,46 @@ const mapDispatchToProps = (dispatch: React.Dispatch<ActionType<{}>>): AppDispat
};
};

class App extends React.Component<AppPropModel & RouteComponentProps<{}> & AppDispatchPropModel> {
private updateClockTimer: NodeJS.Timeout;
private updateLoadAndModulesTimer: NodeJS.Timeout;
function App(props: AppPropModel & AppDispatchPropModel) {
const updateClockTimerRef = React.useRef<NodeJS.Timeout>();
const updateLoadAndModulesTimerRef = React.useRef<NodeJS.Timeout>();

constructor(props: AppPropModel & RouteComponentProps<{}> & AppDispatchPropModel) {
super(props);
React.useEffect(() => {
updateLoadAndModulesTimerRef.current = setInterval(loadAndModulesUpdater, 5000);
props.ModulesRestClient.modules().then((data) => props?.onUpdateModules(data));

this.loadAndModulesUpdater = this.loadAndModulesUpdater.bind(this);
}
return () => {
clearInterval(updateClockTimerRef.current!);
clearInterval(updateLoadAndModulesTimerRef.current!);
};
}, []);

public componentDidMount(): void {
this.updateLoadAndModulesTimer = setInterval(this.loadAndModulesUpdater, 5000);
this.props.ModulesRestClient.modules().then((data) => this.props?.onUpdateModules(data));
}

public componentWillUnmount(): void {
clearInterval(this.updateClockTimer);
clearInterval(this.updateLoadAndModulesTimer);
}

public render(): React.ReactNode {
return (
<div className="commandcenter-app-container">
<div className="commandcenter-content-wrapper">
<ToastContainer />

<Container fluid={true} id="body" className="content">
<Switch>
<Route path="/modules" component={Modules} />
<Route path="/databases" component={Databases} />
<Route render={() => <Redirect to="/databases" />} />
</Switch>
</Container>
</div>
</div>
);
}

private loadAndModulesUpdater(): void {
this.props.Modules.forEach((module) => {
this.props.ModulesRestClient.healthState(module.name).then((data) =>
this.props.onUpdateModuleHealthState(module.name, data)
const loadAndModulesUpdater = (): void => {
props.Modules.forEach((module) => {
props.ModulesRestClient.healthState(module.name).then((data) =>
props.onUpdateModuleHealthState(module.name, data)
);
this.props.ModulesRestClient.notifications(module.name).then((data) =>
this.props.onUpdateModuleNotifications(module.name, data)
props.ModulesRestClient.notifications(module.name).then((data) =>
props.onUpdateModuleNotifications(module.name, data)
);
});
}
};

return (
<div className="commandcenter-app-container">
<div className="commandcenter-content-wrapper">
<ToastContainer />

<Container fluid={true} id="body" className="content">
<Routes>
<Route path="/modules/*" element={<Modules />} />
<Route path="/databases/*" element={<Databases />} />
<Route path="*" element={<Navigate to="/databases/*" />} />
</Routes>
</Container>
</div>
</div>
);
}

export default withRouter<RouteComponentProps<{}>, React.ComponentType<any>>(
connect<AppPropModel, AppDispatchPropModel>(mapStateToProps, mapDispatchToProps)(App)
);
export default connect(mapStateToProps, mapDispatchToProps)(App);
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
import { mdiBriefcase, mdiCheck, mdiDatabase, mdiExclamationThick, mdiLoading, mdiPowerPlug, mdiTable } from "@mdi/js";
import Icon from "@mdi/react";
import * as moment from "moment";
import { any, element, string } from "prop-types";
import * as React from "react";
import { connect, Provider } from "react-redux";
import { RouteComponentProps, withRouter } from "react-router-dom";
import { toast, ToastContainer } from "react-toastify";
import { connect } from "react-redux";
import { toast } from "react-toastify";
import { Button, ButtonGroup, Card, CardBody, CardHeader, Col, Container, Form, Input, Nav, NavItem, NavLink, Row, TabContent, TabPane, UncontrolledTooltip } from "reactstrap";
import kbToString from "../../common/converter/ByteConverter";
import { updateShowWaitDialog } from "../../common/redux/CommonActions";
Expand Down
Loading
Loading