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 /node_modules
.gitignore
# 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*

35898
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", "version": "0.2.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"bootstrap": "^4.6.1", "bootstrap": "^5.1.3",
"formik": "^2.2.9", "formik": "^2.2.9",
"prop-types": "^15.7.2", "prop-types": "^15.8.1",
"react": "^17.0.2", "react": "^18.0.0",
"react-dom": "^17.0.2", "react-dom": "^18.0.0",
"react-player": "^2.9.0", "react-player": "^2.10.0",
"react-redux": "^7.2.6", "react-redux": "^8.0.0",
"react-router-dom": "^5.2.1", "react-router": "^6.3.0",
"reactstrap": "^8.10.1", "react-router-dom": "^6.3.0",
"reactstrap": "^9.0.2",
"redux": "^4.1.2", "redux": "^4.1.2",
"redux-thunk": "^2.4.0", "redux-thunk": "^2.4.0",
"superagent": "^6.1.0", "superagent": "^7.1.2",
"toastr": "^2.1.4" "toastr": "^2.1.4"
}, },
"devDependencies": { "devDependencies": {
"react-scripts": "4.0.3" "react-scripts": "^5.0.1"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "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 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. Updated Spring 2021 to React 17.0.2 and latest version of all other packages.
- added support for video player - 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 React, { useEffect } from 'react';
import { Redirect } from 'react-router-dom'; import { Navigate } from 'react-router-dom';
import store from '../store'; import store from '../store';
const Logout = () => { const Logout = () => {
// normally, we would inform the server just in case. // 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="; document.cookie = "auth_token=";
useEffect(() => { useEffect(() => {
store.dispatch({ type: "LOGOUT" }); store.dispatch({ type: "LOGOUT" });
}, []); }, []);
return (<Redirect to="/" />); return (<Navigate to="/" />);
}; };
export default Logout; export default Logout;

View File

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

View File

@ -34,4 +34,7 @@ const apiPrefix = `${publicUrl}/api`
console.log(`Read configuration. Public_URL: ${publicUrl}`) console.log(`Read configuration. Public_URL: ${publicUrl}`)
// console.log(`apiPrefix: ${apiPrefix}`) // 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 PrivatePage from '../pages/PrivatePage';
import PlayerPage from '../pages/PlayerPage'; 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 /** AppContainer renders the navigation bar on top and its
* children in the main part of the page. Its children will * children in the main part of the page. Its children will
@ -19,22 +19,22 @@ import config from '../config/';
*/ */
const AppContainer = (props) => ( const AppContainer = (props) => (
<div> <div>
<TopNavBar branding="CS3214 Demo App 2021" <TopNavBar branding={config.branding}
menus={config.menus} menus={config.menus}
user={props.user} user={props.user}
loginUrl={`/login`} loginUrl={`/login`}
logoutUrl={`/logout`} logoutUrl={`/logout`}
/> />
<div className="container-fluid marketing"> <div className="container-fluid marketing">
<Switch> <Routes>
<Route exact path={`/`} component={HomePage} /> <Route exact path={`/`} element={<HomePage />} />
<Route path={`/logout`} component={Logout} /> <Route path={`/logout`} element={<Logout />} />
<Route path={`/login`} component={LoginPage} /> <Route path={`/login`} element={<LoginPage />} />
<Route path={`/public`} component={PublicPage} /> <Route path={`/public`} element={<PublicPage />} />
<Route path={`/protected`} component={PrivatePage} /> <Route path={`/protected`} element={<PrivatePage />} />
<Route path={`/player`} component={PlayerPage} /> <Route path={`/player`} element={<PlayerPage />} />
<Route component={NotFoundPage} /> <Route element={<NotFoundPage />} />
</Switch> </Routes>
</div> </div>
</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 React from 'react';
import { Redirect, withRouter } from 'react-router'; import { Navigate, useLocation } from 'react-router';
import { connect } from 'react-redux'; import { useSelector } from 'react-redux';
import { isLoaded } from '../util/loadingObject'; import { isLoaded } from '../util/loadingObject';
function mapStateToProps(state) {
return {
user: state.auth
};
}
export default function RequireAuthentication(Component) { export default function RequireAuthentication(Component) {
const wrapper = props => { const wrapper = props => {
if (isLoaded(props.user)) { const location = useLocation();
const user = useSelector(state => state.auth);
if (isLoaded(user)) {
return <Component {...props} />; return <Component {...props} />;
} else { } else {
return ( return (
<Redirect <Navigate
to={{ to={`/login`}
pathname: `/login`, state = {{
state: { from: location
from: props.history.location
}
}} }}
/> />
); );
} }
}; };
return withRouter(connect(mapStateToProps)(wrapper)); return wrapper;
} }

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom'; import { BrowserRouter as Router } from 'react-router-dom';
@ -22,6 +23,7 @@ toastr.options.positionClass = 'toast-bottom-right';
store.dispatch(checklogin()); store.dispatch(checklogin());
const mountPoint = document.getElementById('root'); const mountPoint = document.getElementById('root');
const root = createRoot(mountPoint);
const rootNode = ( const rootNode = (
<Provider store={store}> <Provider store={store}>
<Router basename={config.publicUrl}> <Router basename={config.publicUrl}>
@ -29,6 +31,6 @@ const rootNode = (
</Router> </Router>
</Provider> </Provider>
); );
ReactDOM.render(rootNode, mountPoint); root.render(rootNode);
window.jQuery = jQuery; // for toastr 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 { login } from '../actions/auth.js';
import { isLoading, isLoaded } from '../util/loadingObject' import { isLoading, isLoaded } from '../util/loadingObject'
import LoginForm from '../components/forms/LoginForm'; 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(); const dispatch = useDispatch();
function doLogin({username, password}) { function doLogin({username, password}) {
dispatch(login(username, password)); dispatch(login(username, password));
@ -15,9 +16,9 @@ const LoginPage = (props) => {
const user = useSelector(state => state.auth); const user = useSelector(state => state.auth);
const isAuthenticated = isLoaded(user); const isAuthenticated = isLoaded(user);
const { from } = props.location.state || { from: { pathname: "/" } }; const { from } = location.state || { from: { pathname: "/" } };
if (isAuthenticated) { if (isAuthenticated) {
return (<Redirect to={from} />); return (<Navigate to={from} />);
} }
return ( return (

View File

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

View File

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