update to current npm versions (React 18, BS5, etc.)

This commit is contained in:
Godmar Back 2022-04-19 09:53:48 -04:00
parent 5555adff24
commit be04f7d339
13 changed files with 22643 additions and 13475 deletions

21
react-app/.gitignore vendored
View File

@ -1,21 +1,2 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.gitignore

35954
react-app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,22 +3,23 @@
"version": "0.2.0",
"private": true,
"dependencies": {
"bootstrap": "^4.6.1",
"bootstrap": "^5.1.3",
"formik": "^2.2.9",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-player": "^2.9.0",
"react-redux": "^7.2.6",
"react-router-dom": "^5.2.1",
"reactstrap": "^8.10.1",
"prop-types": "^15.8.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-player": "^2.10.0",
"react-redux": "^8.0.0",
"react-router": "^6.3.0",
"react-router-dom": "^6.3.0",
"reactstrap": "^9.0.2",
"redux": "^4.1.2",
"redux-thunk": "^2.4.0",
"superagent": "^6.1.0",
"superagent": "^7.1.2",
"toastr": "^2.1.4"
},
"devDependencies": {
"react-scripts": "4.0.3"
"react-scripts": "^5.0.1"
},
"scripts": {
"start": "react-scripts start",

View File

@ -1,7 +1,11 @@
https://reacttraining.com/react-router/web/guides/redux-integration
Updated Spring 2020 to React 16.13.1 and react-redux 7.2.0
Updated Spring 2021 to React 17.0.2 and latest version of all other packages.
- added support for video player
Updated Spring 2022 to React 18.0.0 and latest version of all other packages.
- updated bootstrap to BS5 and react-router v6
https://reacttraining.com/react-router/web/guides/redux-integration

View File

@ -1,14 +1,15 @@
import React, { useEffect } from 'react';
import { Redirect } from 'react-router-dom';
import { Navigate } from 'react-router-dom';
import store from '../store';
const Logout = () => {
// normally, we would inform the server just in case.
// (also, this wouldn't work if the cookie were httponly which it ought to be
document.cookie = "auth_token=";
useEffect(() => {
store.dispatch({ type: "LOGOUT" });
}, []);
return (<Redirect to="/" />);
return (<Navigate to="/" />);
};
export default Logout;

View File

@ -38,10 +38,10 @@ const DropDowns = (props) => {
<DropdownToggle nav caret>
{dropdown.label}
</DropdownToggle>
<DropdownMenu right>
<DropdownMenu end>
{dropdown.entries.map((item) =>
<DropdownItem key={item.path}>
<NavLink to={item.path} key={item.path} activeClassName="active" tag={RRNavLink}>
<NavLink to={item.path} key={item.path} tag={RRNavLink}>
{item.label}
</NavLink>
</DropdownItem>
@ -70,7 +70,7 @@ const NavBar = (props) => {
<Nav className="mr-auto" navbar>
{menus.topbar.map((item) =>
<NavItem key={item.path}>
<NavLink to={item.path} activeClassName="active" tag={RRNavLink}>
<NavLink to={item.path} tag={RRNavLink}>
{item.label}
</NavLink>
</NavItem>
@ -86,11 +86,11 @@ const NavBar = (props) => {
{isLoaded(user) ?
<NavItem>
<NavLink activeClassName="active" tag={RRNavLink} to={props.logoutUrl}>Logout ({user.sub})</NavLink>
<NavLink tag={RRNavLink} to={props.logoutUrl}>Logout ({user.sub})</NavLink>
</NavItem>
:
<NavItem>
<NavLink activeClassName="active" tag={RRNavLink} to={props.loginUrl}>Login</NavLink>
<NavLink tag={RRNavLink} to={props.loginUrl}>Login</NavLink>
</NavItem>
}
</Nav>

View File

@ -34,4 +34,7 @@ const apiPrefix = `${publicUrl}/api`
console.log(`Read configuration. Public_URL: ${publicUrl}`)
// console.log(`apiPrefix: ${apiPrefix}`)
export default { menus, apiPrefix, publicUrl }
export default { menus,
branding: "CS3214 Demo App 2022",
apiPrefix, publicUrl
}

View File

@ -9,9 +9,9 @@ import HomePage from '../pages/HomePage';
import PrivatePage from '../pages/PrivatePage';
import PlayerPage from '../pages/PlayerPage';
import { Switch, Route, withRouter } from 'react-router-dom';
import { Routes, Route } from 'react-router-dom';
import config from '../config/';
import config from '../config';
/** AppContainer renders the navigation bar on top and its
* children in the main part of the page. Its children will
@ -19,22 +19,22 @@ import config from '../config/';
*/
const AppContainer = (props) => (
<div>
<TopNavBar branding="CS3214 Demo App 2021"
<TopNavBar branding={config.branding}
menus={config.menus}
user={props.user}
loginUrl={`/login`}
logoutUrl={`/logout`}
/>
<div className="container-fluid marketing">
<Switch>
<Route exact path={`/`} component={HomePage} />
<Route path={`/logout`} component={Logout} />
<Route path={`/login`} component={LoginPage} />
<Route path={`/public`} component={PublicPage} />
<Route path={`/protected`} component={PrivatePage} />
<Route path={`/player`} component={PlayerPage} />
<Route component={NotFoundPage} />
</Switch>
<Routes>
<Route exact path={`/`} element={<HomePage />} />
<Route path={`/logout`} element={<Logout />} />
<Route path={`/login`} element={<LoginPage />} />
<Route path={`/public`} element={<PublicPage />} />
<Route path={`/protected`} element={<PrivatePage />} />
<Route path={`/player`} element={<PlayerPage />} />
<Route element={<NotFoundPage />} />
</Routes>
</div>
</div>
);
@ -51,4 +51,4 @@ function mapDispatchToProps(dispatch) {
};
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(AppContainer));
export default connect(mapStateToProps, mapDispatchToProps)(AppContainer);

View File

@ -4,33 +4,27 @@
*/
import React from 'react';
import { Redirect, withRouter } from 'react-router';
import { connect } from 'react-redux';
import { Navigate, useLocation } from 'react-router';
import { useSelector } from 'react-redux';
import { isLoaded } from '../util/loadingObject';
function mapStateToProps(state) {
return {
user: state.auth
};
}
export default function RequireAuthentication(Component) {
const wrapper = props => {
if (isLoaded(props.user)) {
const location = useLocation();
const user = useSelector(state => state.auth);
if (isLoaded(user)) {
return <Component {...props} />;
} else {
return (
<Redirect
to={{
pathname: `/login`,
state: {
from: props.history.location
}
}}
<Navigate
to={`/login`}
state = {{
from: location
}}
/>
);
}
};
return withRouter(connect(mapStateToProps)(wrapper));
return wrapper;
}

View File

@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
@ -22,6 +23,7 @@ toastr.options.positionClass = 'toast-bottom-right';
store.dispatch(checklogin());
const mountPoint = document.getElementById('root');
const root = createRoot(mountPoint);
const rootNode = (
<Provider store={store}>
<Router basename={config.publicUrl}>
@ -29,6 +31,6 @@ const rootNode = (
</Router>
</Provider>
);
ReactDOM.render(rootNode, mountPoint);
root.render(rootNode);
window.jQuery = jQuery; // for toastr

View File

@ -5,9 +5,10 @@ import { Card, CardHeader, CardBody, Container, Row, Col } from 'reactstrap';
import { login } from '../actions/auth.js';
import { isLoading, isLoaded } from '../util/loadingObject'
import LoginForm from '../components/forms/LoginForm';
import { Redirect } from 'react-router-dom';
import { Navigate, useLocation } from 'react-router-dom';
const LoginPage = (props) => {
const LoginPage = () => {
const location = useLocation();
const dispatch = useDispatch();
function doLogin({username, password}) {
dispatch(login(username, password));
@ -15,9 +16,9 @@ const LoginPage = (props) => {
const user = useSelector(state => state.auth);
const isAuthenticated = isLoaded(user);
const { from } = props.location.state || { from: { pathname: "/" } };
const { from } = location.state || { from: { pathname: "/" } };
if (isAuthenticated) {
return (<Redirect to={from} />);
return (<Navigate to={from} />);
}
return (
@ -38,4 +39,4 @@ const LoginPage = (props) => {
</Container>);
}
export default LoginPage;
export default LoginPage;

View File

@ -14,8 +14,8 @@ const PlayerPage = () => {
const dispatch = useDispatch();
let videos = useSelector((state) => state.video);
let [state, setState] = useState({
const videos = useSelector((state) => state.video);
const [state, setState] = useState({
url: ""
});
const handleChange = (event) => {
@ -61,7 +61,10 @@ const PlayerPage = () => {
</ButtonToolbar>
</Col>
<Col>
or select one from the list of
<FormGroup>
<Label for="ddmenu">
or select one from the list of
</Label>
{!isLoaded(videos) ? (
<Spinner size="lg" color="primary" />
) : (
@ -79,6 +82,7 @@ const PlayerPage = () => {
</DropdownMenu>
</Dropdown>
)}
</FormGroup>
</Col>
</Row>
</Container>

View File

@ -9,16 +9,19 @@ const PrivatePage = () => {
return (<Container>
<h1>Welcome to a private page</h1>
<Row className="mt-3">
You have successfully authenticated as user&nbsp;<span>{user.sub}</span>.
<p>
You have successfully authenticated as user&nbsp;<tt>{user.sub}</tt>.
</p>
</Row>
<Row className="mt-3">
Your token was issued at {new Date(user.iat*1000).toString()},
it expires {new Date(user.exp*1000).toString()}
<p>Your token was issued at {new Date(user.iat*1000).toString()},
it expires {new Date(user.exp*1000).toString()}</p>
</Row>
<Row className="mt-3">
This page is "private" only inasmuch as the front-end does not
display it to unauthenticated users. In a fully-fledged app,
this page would now perform API requests that require authentication.
<p> This page is "private" only inasmuch as the front-end does not
display it to unauthenticated users. In a fully-fledged app,
this page would now perform API requests that require authentication.
</p>
</Row>
</Container>);
}