release notes page

master
Tudor Stanciu 2020-05-14 15:29:32 +03:00
parent 3a7613e9ff
commit de4f0c5b4f
26 changed files with 245 additions and 82 deletions

View File

@ -15,6 +15,7 @@
"Menu": { "Menu": {
"Home": "Home", "Home": "Home",
"Sessions": "Sessions", "Sessions": "Sessions",
"ReleaseNotes": "Release notes",
"About": "About" "About": "About"
}, },
"General": { "General": {
@ -43,5 +44,9 @@
"PathOverwriteTooltip": "Option by which the base path of the application is automatically overwritten in all http text responses received from it." "PathOverwriteTooltip": "Option by which the base path of the application is automatically overwritten in all http text responses received from it."
} }
} }
},
"ReleaseNotes": {
"Title": "Release notes",
"Version": "Version"
} }
} }

View File

@ -6,6 +6,7 @@
"Menu": { "Menu": {
"Home": "Acasă", "Home": "Acasă",
"Sessions": "Sesiuni", "Sessions": "Sesiuni",
"ReleaseNotes": "Note lansare",
"About": "Despre" "About": "Despre"
}, },
"General": { "General": {
@ -34,5 +35,9 @@
"PathOverwriteTooltip": "Opțiune prin care calea de bază a aplicației este suprascrisă automat în toate răspunsurile de text http primite de la aceasta." "PathOverwriteTooltip": "Opțiune prin care calea de bază a aplicației este suprascrisă automat în toate răspunsurile de text http primite de la aceasta."
} }
} }
},
"ReleaseNotes": {
"Title": "Note lansare",
"Version": "Versiune"
} }
} }

View File

@ -7,6 +7,7 @@ import PageNotFound from "./PageNotFound";
import { ToastContainer } from "react-toastify"; import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css"; import "react-toastify/dist/ReactToastify.css";
import SessionContainer from "../features/session/components/SessionContainer"; import SessionContainer from "../features/session/components/SessionContainer";
import ReleaseNotesContainer from "../features/releaseNotes/components/ReleaseNotesContainer";
function App() { function App() {
const contentStyle = { const contentStyle = {
@ -23,6 +24,7 @@ function App() {
<Route exact path="/" component={HomePage} /> <Route exact path="/" component={HomePage} />
<Route path="/about" component={AboutPage} /> <Route path="/about" component={AboutPage} />
<Route path="/sessions" component={SessionContainer} /> <Route path="/sessions" component={SessionContainer} />
<Route path="/release-notes" component={ReleaseNotesContainer} />
<Route component={PageNotFound} /> <Route component={PageNotFound} />
</Switch> </Switch>
<ToastContainer autoClose={3000} /> <ToastContainer autoClose={3000} />

View File

@ -0,0 +1,11 @@
const styles = (theme) => ({
root: {
width: "100%"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
}
});
export default styles;

View File

@ -5,15 +5,13 @@ import { bindActionCreators } from "redux";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { import {
loadSystemDateTime, loadSystemDateTime,
loadSystemVersion, loadSystemVersion
loadReleaseNotes
} from "../../features/system/actionCreators"; } from "../../features/system/actionCreators";
const HomePage = ({ actions }) => { const HomePage = ({ actions }) => {
const testButton = () => { const testButton = () => {
actions.loadSystemDateTime(); actions.loadSystemDateTime();
actions.loadSystemVersion(); actions.loadSystemVersion();
actions.loadReleaseNotes();
}; };
return ( return (
@ -46,7 +44,7 @@ function mapStateToProps() {
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
actions: bindActionCreators( actions: bindActionCreators(
{ loadSystemDateTime, loadSystemVersion, loadReleaseNotes }, { loadSystemDateTime, loadSystemVersion },
dispatch dispatch
) )
}; };

View File

@ -17,6 +17,10 @@ const Navigation = () => {
{t("Menu.Sessions")} {t("Menu.Sessions")}
</NavLink> </NavLink>
{" | "} {" | "}
<NavLink to="/release-notes" activeStyle={activeStyle} style={style}>
{t("Menu.ReleaseNotes")}
</NavLink>
{" | "}
<NavLink to="/about" activeStyle={activeStyle} style={style}> <NavLink to="/about" activeStyle={activeStyle} style={style}>
{t("Menu.About")} {t("Menu.About")}
</NavLink> </NavLink>

View File

@ -0,0 +1,18 @@
import * as types from "./actionTypes";
import api from "./api";
import { sendHttpRequest } from "../../redux/actions/httpActions";
export function loadReleaseNotes() {
return async function (dispatch, getState) {
try {
const notes = getState().releaseNotes;
if (notes && (notes.loading || notes.loaded)) return;
dispatch({ type: types.LOAD_RELEASE_NOTES_STARTED });
const data = await dispatch(sendHttpRequest(api.getReleaseNotes()));
dispatch({ type: types.LOAD_RELEASE_NOTES_SUCCESS, payload: data });
} catch (error) {
throw error;
}
};
}

View File

@ -0,0 +1,2 @@
export const LOAD_RELEASE_NOTES_STARTED = "LOAD_RELEASE_NOTES_STARTED";
export const LOAD_RELEASE_NOTES_SUCCESS = "LOAD_RELEASE_NOTES_SUCCESS";

View File

@ -0,0 +1,8 @@
import { get } from "../../api/axiosApi";
const baseUrl = process.env.REVERSE_PROXY_API_URL + "/system";
const getReleaseNotes = () => get(`${baseUrl}/release-notes`);
export default {
getReleaseNotes
};

View File

@ -0,0 +1,36 @@
import React, { useEffect } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import PropTypes from "prop-types";
import { loadReleaseNotes } from "../actionCreators";
import ReleaseNotesListComponent from "./ReleaseNotesListComponent";
const ReleaseNotesContainer = ({ actions, releaseNotes }) => {
useEffect(() => {
actions.loadReleaseNotes();
}, []);
return <ReleaseNotesListComponent releaseNotes={releaseNotes} />;
};
ReleaseNotesContainer.propTypes = {
actions: PropTypes.object.isRequired,
releaseNotes: PropTypes.array.isRequired
};
function mapStateToProps(state) {
return {
releaseNotes: state.releaseNotes
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({ loadReleaseNotes }, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ReleaseNotesContainer);

View File

@ -0,0 +1,50 @@
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import ExpansionPanel from "@material-ui/core/ExpansionPanel";
import ExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary";
import ExpansionPanelDetails from "@material-ui/core/ExpansionPanelDetails";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import PropTypes from "prop-types";
import Spinner from "../../../components/common/Spinner";
import { useTranslation } from "react-i18next";
import ReleaseNotesSummary from "./ReleaseNotesSummary";
import styles from "../../../components/common/styles/divStyles";
const useStyles = makeStyles(styles);
const ReleaseNotesListComponent = ({ releaseNotes }) => {
const classes = useStyles();
const { t } = useTranslation();
return (
<div className={classes.root}>
<h2>{t("ReleaseNotes.Title")}</h2>
{releaseNotes.loading ? (
<Spinner />
) : (
releaseNotes.loaded &&
releaseNotes.map((note) => {
return (
<ExpansionPanel key={note.version}>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
id={`panel-${note.version}-header`}
>
<ReleaseNotesSummary releaseNote={note} />
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<div>TO DO</div>
</ExpansionPanelDetails>
</ExpansionPanel>
);
})
)}
</div>
);
};
ReleaseNotesListComponent.propTypes = {
releaseNotes: PropTypes.array.isRequired
};
export default ReleaseNotesListComponent;

View File

@ -0,0 +1,33 @@
import React from "react";
import PropTypes from "prop-types";
import { Grid } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import { useTranslation } from "react-i18next";
import styles from "../../../components/common/styles/gridStyles";
const useStyles = makeStyles(styles);
const ReleaseNotesSummary = ({ releaseNote }) => {
const classes = useStyles();
const { t } = useTranslation();
return (
<Grid container className={classes.miniContainer}>
<Grid item xs={12} sm={5} md={5}>
{`${t("ReleaseNotes.Version")}: `}
<span className={classes.value}>{releaseNote.version}</span>
</Grid>
<Grid item xs={12} sm={5} md={5}>
{`${t("ReleaseNotes.Version")}: `}
<span className={classes.value}>{releaseNote.version}</span>
</Grid>
</Grid>
);
};
ReleaseNotesSummary.propTypes = {
releaseNote: PropTypes.object.isRequired
};
export default ReleaseNotesSummary;

View File

@ -0,0 +1,18 @@
import * as types from "./actionTypes";
import initialState from "../../redux/reducers/initialState";
export default function releaseNotesReducer(
state = initialState.releaseNotes,
action
) {
switch (action.type) {
case types.LOAD_RELEASE_NOTES_STARTED:
return Object.assign([], { loading: true, loaded: false });
case types.LOAD_RELEASE_NOTES_SUCCESS:
return Object.assign(action.payload, { loading: false, loaded: true });
default:
return state;
}
}

View File

@ -9,7 +9,7 @@ import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import Tooltip from "@material-ui/core/Tooltip"; import Tooltip from "@material-ui/core/Tooltip";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import styles from "../styles/tableStyles"; import styles from "../../../components/common/styles/tableStyles";
import { import {
StyledTableCell, StyledTableCell,
StyledTableRow StyledTableRow

View File

@ -14,7 +14,7 @@ import Tooltip from "@material-ui/core/Tooltip";
import SessionForwardsHeaderComponent from "./SessionForwardsHeaderComponent"; import SessionForwardsHeaderComponent from "./SessionForwardsHeaderComponent";
import { Grid } from "@material-ui/core"; import { Grid } from "@material-ui/core";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import styles from "../styles/tableStyles"; import styles from "../../../components/common/styles/tableStyles";
import { import {
StyledTableCell, StyledTableCell,
StyledTableRow StyledTableRow

View File

@ -3,7 +3,7 @@ import PropTypes from "prop-types";
import { Grid } from "@material-ui/core"; import { Grid } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import styles from "../styles/gridStyles"; import styles from "../../../components/common/styles/gridStyles";
const useStyles = makeStyles(styles); const useStyles = makeStyles(styles);

View File

@ -9,16 +9,9 @@ import SessionSummary from "./SessionSummary";
import SessionForwardsComponent from "./SessionForwardsComponent"; import SessionForwardsComponent from "./SessionForwardsComponent";
import Spinner from "../../../components/common/Spinner"; import Spinner from "../../../components/common/Spinner";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import styles from "../../../components/common/styles/divStyles";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles(styles);
root: {
width: "100%"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
}
}));
const SessionListComponent = ({ const SessionListComponent = ({
sessions, sessions,

View File

@ -4,7 +4,7 @@ import { Grid } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import ActiveIcon from "../../../components/common/ActiveIcon"; import ActiveIcon from "../../../components/common/ActiveIcon";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import styles from "../styles/gridStyles"; import styles from "../../../components/common/styles/gridStyles";
const useStyles = makeStyles(styles); const useStyles = makeStyles(styles);
@ -13,50 +13,48 @@ const SessionSummary = ({ session }) => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<> <Grid container className={classes.miniContainer}>
<Grid container className={classes.miniContainer}> <Grid item xs={12} sm={5} md={5}>
<Grid item xs={12} sm={5} md={5}> {`${t("Session.Session")}: `}
{`${t("Session.Session")}: `} <span className={classes.value}>{session.sessionId}</span>
<span className={classes.value}>{session.sessionId}</span>
</Grid>
<Grid item xs={12} sm={3} md={3}>
{`${t("Session.Active")}: `}
<ActiveIcon active={session.active} />
</Grid>
<Grid item xs={12} sm={4} md={4}>
{`${t("Session.StartDate")}: `}
<span className={classes.value}>
{t("DATE_FORMAT", {
date: { value: session.startDate, format: "DD-MM-YYYY HH:mm:ss" }
})}
</span>
</Grid>
<Grid item xs={12} sm={5} md={5}>
{`${t("Session.RunningTime")}: `}
<span className={classes.value}>{session.runningTime}</span>
</Grid>
<Grid item xs={12} sm={3} md={3}>
{`${t("Session.Host")}: `}
<span className={classes.value}>{session.hostName}</span>
</Grid>
<Grid item xs={12} sm={4} md={4}>
{`${t("Session.StopDate")}: `}
<span className={classes.value}>
{session.stopDate
? t("DATE_FORMAT", {
date: {
value: session.stopDate,
format: "DD-MM-YYYY HH:mm:ss"
}
})
: "---"}
</span>
</Grid>
</Grid> </Grid>
</> <Grid item xs={12} sm={3} md={3}>
{`${t("Session.Active")}: `}
<ActiveIcon active={session.active} />
</Grid>
<Grid item xs={12} sm={4} md={4}>
{`${t("Session.StartDate")}: `}
<span className={classes.value}>
{t("DATE_FORMAT", {
date: { value: session.startDate, format: "DD-MM-YYYY HH:mm:ss" }
})}
</span>
</Grid>
<Grid item xs={12} sm={5} md={5}>
{`${t("Session.RunningTime")}: `}
<span className={classes.value}>{session.runningTime}</span>
</Grid>
<Grid item xs={12} sm={3} md={3}>
{`${t("Session.Host")}: `}
<span className={classes.value}>{session.hostName}</span>
</Grid>
<Grid item xs={12} sm={4} md={4}>
{`${t("Session.StopDate")}: `}
<span className={classes.value}>
{session.stopDate
? t("DATE_FORMAT", {
date: {
value: session.stopDate,
format: "DD-MM-YYYY HH:mm:ss"
}
})
: "---"}
</span>
</Grid>
</Grid>
); );
}; };

View File

@ -23,14 +23,3 @@ export function loadSystemVersion() {
} }
}; };
} }
export function loadReleaseNotes() {
return async function (dispatch) {
try {
const data = await dispatch(sendHttpRequest(api.getReleaseNotes()));
dispatch({ type: types.LOAD_RELEASE_NOTES_SUCCESS, payload: data });
} catch (error) {
throw error;
}
};
}

View File

@ -1,3 +1,2 @@
export const LOAD_SYSTEM_DATETIME_SUCCESS = "LOAD_SYSTEM_DATETIME_SUCCESS"; export const LOAD_SYSTEM_DATETIME_SUCCESS = "LOAD_SYSTEM_DATETIME_SUCCESS";
export const LOAD_SYSTEM_VERSION_SUCCESS = "LOAD_SYSTEM_VERSION_SUCCESS"; export const LOAD_SYSTEM_VERSION_SUCCESS = "LOAD_SYSTEM_VERSION_SUCCESS";
export const LOAD_RELEASE_NOTES_SUCCESS = "LOAD_RELEASE_NOTES_SUCCESS";

View File

@ -3,10 +3,8 @@ const baseUrl = process.env.REVERSE_PROXY_API_URL + "/system";
const getSystemDateTime = () => get(`${baseUrl}/datetime`); const getSystemDateTime = () => get(`${baseUrl}/datetime`);
const getSystemVersion = () => get(`${baseUrl}/version`); const getSystemVersion = () => get(`${baseUrl}/version`);
const getReleaseNotes = () => get(`${baseUrl}/release-notes`);
export default { export default {
getSystemDateTime, getSystemDateTime,
getSystemVersion, getSystemVersion
getReleaseNotes
}; };

View File

@ -14,13 +14,6 @@ export default function systemReducer(state = initialState.system, action) {
...state, ...state,
...action.payload ...action.payload
}; };
case types.LOAD_RELEASE_NOTES_SUCCESS:
return {
...state,
releaseNotes: action.payload
};
default: default:
return state; return state;
} }

View File

@ -5,11 +5,13 @@ import {
sessionsReducer, sessionsReducer,
forwardsReducer forwardsReducer
} from "../../features/session/reducers"; } from "../../features/session/reducers";
import releaseNotesReducer from "../../features/releaseNotes/reducer";
const rootReducer = combineReducers({ const rootReducer = combineReducers({
system: systemReducer, system: systemReducer,
sessions: sessionsReducer, sessions: sessionsReducer,
forwards: forwardsReducer, forwards: forwardsReducer,
releaseNotes: releaseNotesReducer,
ajaxCallsInProgress: ajaxStatusReducer ajaxCallsInProgress: ajaxStatusReducer
}); });

View File

@ -2,5 +2,6 @@ export default {
system: {}, system: {},
sessions: Object.assign([], { loading: false, loaded: false }), sessions: Object.assign([], { loading: false, loaded: false }),
forwards: {}, forwards: {},
releaseNotes: Object.assign([], { loading: false, loaded: false }),
ajaxCallsInProgress: 0 ajaxCallsInProgress: 0
}; };