ソースを参照

frontendreducer

Gennadysht 2 年 前
コミット
06a1a301ba

+ 13 - 33
src/App.js

@@ -1,47 +1,31 @@
-import React, { useEffect, useState } from 'react';
+
 import { Router, Route, Switch, useParams } from 'react-router-dom';
 import { createBrowserHistory } from "history";
 import { createStore, combineReducers, applyMiddleware } from 'redux';
-import { Provider, connect, useSelector } from 'react-redux';
-import './App.css';
-import { authReducer, promiseReducer, actionAuthLogin, actionAuthLoginThunk } from './reducers';
-import { CLoginForm, GoodExample, GoodsList, goodsExample, Category, exampleCategory, OrderGood, exampleOrderGood, Order, exampleOrder, OrderList, exampleOrderList, exampleOrderGoodsList, OrderGoodsList } from "./Components";
-import { MainAppBar } from './Components';
+import { Provider} from 'react-redux';
+import { authReducer, promiseReducer, actionAuthLogin, frontEndReducer } from './reducers';
+import { CLoginForm, CMainAppBar } from "./Components";
 import { CLogout } from './Components';
-import { Sidebar } from './Components/Sidebar';
+import { CSidebar } from './Components/Sidebar';
 import thunk from 'redux-thunk';
 
-
-export const store = createStore(combineReducers({ promise: promiseReducer, auth: authReducer }), applyMiddleware(thunk));
-store.subscribe(() => console.log(store.getState()))
-
-
+import './App.css';
 
 export const history = createBrowserHistory();
-console.log(useParams)
 
-//store.dispatch(actionRootCats())
-//store.dispatch(actionAuthLogin(localStorage.authToken));
+export const store = createStore(combineReducers({ promise: promiseReducer, auth: authReducer, frontend: frontEndReducer }), applyMiddleware(thunk));
+store.subscribe(() => console.log(store.getState()))
 
+//console.log(useParams)
+store.dispatch(actionAuthLogin(localStorage.authToken));
+console.log('TTTTT' + performance.now())
 
-/*
-const CCatMenu = connect(state => ({ cats: state.promise?.rootCats?.payload || [] }), { onLogin: actionFullLogin })(CatMenu)
-*/
 
 const NotFound = () =>
   <div>
     <h1>404 not found</h1>
   </div>
 
-const Test = () => {
-  let state = useSelector(state => state);
-  let stateAuth = state.auth;
-  let [auth, setAuth] = useState('');
-  if (stateAuth != auth) {
-    auth = auth;
-  }
-  return <div />
-}
 
 const Main = () =>
   <div>
@@ -49,19 +33,15 @@ const Main = () =>
   </div>
 
 
-store.dispatch(actionAuthLoginThunk(localStorage.authToken));
-console.log(performance.now())
-
 function App() {
 
   return (
     <>
       <Router history={history}>
         <Provider store={store}>
-          <Test />
           <div className="App">
-            <MainAppBar />
-            <Sidebar menuComponent={() => <div>TEST!!!!!!</div>} />
+            <CMainAppBar />
+            <CSidebar menuComponent={() => <div>TEST!!!!!!</div>} />
             <Switch>
               <Route path="/" component={Main} exact />
               <Route path="/login" component={CLoginForm} />

+ 38 - 0
src/App2.css

@@ -0,0 +1,38 @@
+.App {
+  text-align: center;
+}
+
+.App-logo {
+  height: 40vmin;
+  pointer-events: none;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+  .App-logo {
+    animation: App-logo-spin infinite 20s linear;
+  }
+}
+
+.App-header {
+  background-color: #282c34;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: calc(10px + 2vmin);
+  color: white;
+}
+
+.App-link {
+  color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}

+ 236 - 0
src/App2.js

@@ -0,0 +1,236 @@
+import logo from './logo.svg';
+import React, { useState, useEffect, useRef, createElement, Component } from 'react';
+import { Router, Route, Link, Redirect, Switch, useParams } from 'react-router-dom';
+import createHistory from "history/createBrowserHistory";
+import { createStore, combineReducers, applyMiddleware } from 'redux';
+import { Provider, connect, useDispatch, useSelector } from 'react-redux';
+import thunk from 'redux-thunk';
+
+import { actionAuthLogin, actionAuthLoginThunk, authReducer } from './reducers';
+import { CLoginForm } from './Components';
+
+
+import './App2.css';
+import { Login } from '@mui/icons-material';
+import { Button } from '@mui/material';
+
+const history = createHistory()
+//console.log(useParams)
+console.log("TEST!!!!")
+
+// setTimeout(() => history.push('/aboutus'), 5000)
+function getGql(url) {
+    return function gql(query, vars = undefined) {
+        try {
+            let fetchSettings =
+            {
+                method: "POST",
+                headers:
+                {
+                    "Content-Type": "application/json",
+                    "Accept": "application/json"
+                },
+                body: JSON.stringify(
+                    {
+                        query: query,
+                        variables: vars
+                    })
+            };
+            let authToken = window.localStorage.authToken;
+            if (authToken) {
+                fetchSettings.headers["Authorization"] = `Bearer ${authToken}`;
+            }
+            return fetch(url, fetchSettings)
+                .then(res => {
+                    try {
+                        if (!res.ok) {
+                            throw Error(res.statusText);
+                        }
+                        return res.json();
+                    }
+                    catch (error) {
+                        throw error;
+                    }
+
+                });
+        }
+        catch (error) {
+            throw error;
+        }
+    }
+}
+const backendURL = "http://shop-roles.node.ed.asmer.org.ua/"
+const gql = getGql(`${backendURL}graphql`);
+const actionPromise = (name, promise) => {
+    return async (dispatch) => {
+        dispatch(actionPending(name)) //сигнализируем redux, что промис начался
+        try {
+            let payload = await promise //ожидаем промиса
+            if (payload && payload.data)
+                payload = Object.values(payload.data)[0];
+            dispatch(actionFulfilled(name, payload)) //сигнализируем redux, что промис успешно выполнен
+            return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
+        }
+        catch (error) {
+            dispatch(actionRejected(name, error)) //в случае ошибки - сигнализируем redux, что промис несложился
+        }
+    }
+}
+
+const actionPending = (name) => ({ type: 'PROMISE', name: name, status: 'PENDING' });
+const actionFulfilled = (name, payload) => ({ type: 'PROMISE', name: name, payload: payload, status: 'FULFILLED' });
+const actionRejected = (name, error) => ({ type: 'PROMISE', name: name, error: error, status: 'REJECTED' });
+///////////////////////////////////////
+
+const gqlRootCats = () => {
+    const catQuery = `query roots {
+                            CategoryFind(query: "[{\\"parent\\": null }]") {
+                                _id name
+                            }}`;
+    return gql(catQuery);
+}
+const actionRootCats = () =>
+    actionPromise('rootCats', gqlRootCats());
+
+const gqlCategoryFindOne = (_id) => {
+    const catQuery = `query CategoryFindOne($q: String) {
+            CategoryFindOne(query: $q) {
+                _id name
+                parent { _id name }
+                subCategories { _id name }
+                goods { _id name price description 
+                    images { url }
+                }
+            }
+        }`;
+    return gql(catQuery, { q: JSON.stringify([{ _id }]) });
+}
+const actionCategoryFindOne = (id) =>
+    actionPromise('catFindOne', gqlCategoryFindOne(id));
+
+
+function promiseReducer(state = {}, action) {                   // диспетчер обработки
+    if (action) {
+        if (action.type === 'PROMISE') {
+            let newState = { ...state };
+            newState[action.name] = { status: action.status, payload: action.payload, error: action.error };
+            return newState;
+        }
+    }
+    return state;
+}
+
+const store = createStore(combineReducers({ promise: promiseReducer, auth: authReducer }), applyMiddleware(thunk))
+store.subscribe(() => console.log(store.getState()))
+
+store.dispatch(actionRootCats())
+
+const Main = () =>
+    <main>
+        <h1>MAIN</h1>
+    </main>
+
+const AboutUs = () =>
+    <main>
+        <h1>ABOUT US</h1>
+    </main>
+
+const MyLink = ({ activeClassName = 'activeLink', className = '', to, ...props }) => {
+    const [currentPath, setCurrentPath] = useState(window.location.pathname)
+    useEffect(() =>
+        history.listen(({ pathname }) => setCurrentPath(pathname))
+        , [])
+    return (
+        <Link className={`${className} ${to === currentPath ? activeClassName : ''}`} to={to} {...props} />
+    )
+}
+
+const Multiply = () => {
+    const { a, b } = useParams()
+    return (
+        <pre>
+            a * b = {a} * {b} = {+a * +b}
+        </pre>
+    )
+}
+
+const Add = (/*props*/{ match: { params: { a, b } } }) =>
+    <div>
+        a + b = {a} + {b} = {+a + +b}
+        {/*JSON.stringify(props, null, 4)*/}
+        <Multiply />
+    </div>
+
+const NotFound = () =>
+    <div>
+        <h1>404 not found</h1>
+    </div>
+
+const CatMenuItem = ({ cat: { _id, name } }) =>
+    <>
+        <li><MyLink to={`/category/${_id}`}>{name}</MyLink></li>
+        <li><MyLink to={`/testLogin`}>TEST!!!</MyLink></li>
+        <li><Link to={`/testLogin`}><Button color="inherit">Logout</Button></Link></li>
+
+    </>
+
+const CatMenu = ({ cats = [] }) =>
+    <ul>
+        {cats.map(cat => <CatMenuItem cat={cat} />)}
+    </ul>
+
+const mapStateToProps = state => ({ cats: state.promise.rootCats?.payload })
+// const mapDispatchToProps = {onLogin: actionFullLogin}
+
+// const CCatMenu = connect(mapStateToProps,  mapDispatchToProps)(CatMenu)
+
+const CCatMenu = connect(state => ({ cats: state.promise.rootCats?.payload }))(CatMenu)
+
+// const CLoginForm = connect(null, {onLogin: actionFullLogin})(LoginForm)
+
+const GoodCard = ({ _id, name, price, images = [] }) =>
+    <div className='GoodCard'>
+        <MyLink to={`/good/${_id}`}>
+            <h2>{name}</h2>
+            {images && images[0] && <img src={backendURL + images[0].url} />}
+            <span>{price}</span>
+        </MyLink>
+    </div>
+
+
+const PageCategory = ({ loadData, cat: { name = 'loading', goods = [] } = {} }) => {
+    const { _id } = useParams()
+    //const dispatch = useDispatch()
+    //const dispatch = store.dispatch
+    // const cat = useSelector(state => state.promise.catFindOne?.payload)
+    useEffect(() => {
+        loadData(_id)
+        //dispatch(actionCategoryFindOne(_id))
+    }, [_id])
+    return (
+        <div className='PageCategory'>
+            <h1>Категория: {name}</h1>
+            {goods.map(good => <GoodCard key={good._id} {...good} />)}
+        </div>
+    )
+}
+
+const CPageCategory = connect(state => ({ cat: state.promise.catFindOne?.payload }), { loadData: actionCategoryFindOne })(PageCategory)
+
+export function App2() {
+    return (
+        <Router history={history}>
+            <Provider store={store}>
+                <div className="App">
+                    <CCatMenu />
+                    <Switch>
+                        <Route path="/testLogin" component={CLoginForm} />
+                        <Route path="/category/:_id" component={CPageCategory} />
+                        <Route path="*" component={NotFound} />
+                    </Switch>
+                </div>
+            </Provider>
+        </Router>
+    );
+}
+export default App2;

+ 6 - 5
src/Components/LoginForm.js

@@ -5,7 +5,7 @@ import { Container, CssBaseline, TextField, Avatar, Typography, FormControlLabel
 import { Box } from '@mui/system';
 import { connect } from 'react-redux';
 import { actionFullLogin } from '../jql';
-import { actionAuthLoginThunk } from '../reducers';
+import { MyLink } from './MyLink';
 
 const LoginForm = ({ onLogin }) => {
     const [login, setLogin] = useState('');
@@ -54,15 +54,16 @@ const LoginForm = ({ onLogin }) => {
                     control={<Checkbox value="remember" color="primary" />}
                     label="Remember me"
                 />
-                <Button
+                <MyLink
+                    component="button"
                     variant="contained"
                     sx={{ mt: 3, mb: 2 }}
                     fullWidth
                     type="submit"
                     disabled={!isButtonActive}
-                    onClick={() => { onLogin( login, password ) }}>
+                    onClick={() => onLogin( login, password )}>
                     Login...
-                </Button>
+                </MyLink>
                 <Grid container>
                     <Grid item xs>
                         <Link href="#" variant='body2'>
@@ -79,6 +80,6 @@ const LoginForm = ({ onLogin }) => {
         </Container>
     )
 }
-const CLoginForm = connect(null, {onLogin: actionAuthLoginThunk})(LoginForm)
+const CLoginForm = connect(null, { onLogin: actionFullLogin })(LoginForm)
 
 export { CLoginForm };

+ 9 - 12
src/Components/MainAppBar.js

@@ -6,19 +6,15 @@ import Button from '@mui/material/Button';
 import IconButton from '@mui/material/IconButton';
 import MenuIcon from '@mui/icons-material/Menu';
 import { MyLink } from './MyLink';
-import { useState } from 'react';
-import { connect, useSelector } from 'react-redux';
+import { connect } from 'react-redux';
 import { useTheme } from '@emotion/react';
-import { Logout } from '@mui/icons-material';
-
-export function MainAppBar() {
-    const token = useSelector(state => state.auth?.token)
+import { actionSetSidebar } from '../reducers/frontEndReducer';
 
+const MainAppBar = ({ token, openSidebar }) => {
     const theme = useTheme();
-    const [open, setOpen] = useState(false);
-  
+
     const handleDrawerOpen = () => {
-      setOpen(true);
+        openSidebar(true);
     };
 
     let isLoggedIn = token && true;
@@ -41,14 +37,14 @@ export function MainAppBar() {
                     {
                         !isLoggedIn &&
                         <>
-                            <Button color="inherit" href="/login">Login</Button>
-                            <Button color="inherit" href="/register">Register</Button>
+                            <MyLink to="/login"><Button color="inherit">Login</Button></MyLink>
+                            <MyLink to="/register"><Button color="inherit">Register</Button></MyLink>
                         </>
                     }
                     {
                         isLoggedIn &&
                         <>
-                            <Button color="inherit" href="/logout">Logout</Button>
+                            <MyLink to="/logout"><Button color="inherit">Logout</Button></MyLink>
                         </>
                     }
                     <Button color="inherit">Cart</Button>
@@ -58,3 +54,4 @@ export function MainAppBar() {
     );
 }
 
+export const CMainAppBar = connect(state => ({ token: state.auth?.token, sidebarOpened: state.frontend.sidebar.opened }), { openSidebar: actionSetSidebar }) (MainAppBar);

+ 9 - 6
src/Components/Sidebar.js

@@ -18,6 +18,8 @@ import ListItemIcon from '@mui/material/ListItemIcon';
 import ListItemText from '@mui/material/ListItemText';
 import InboxIcon from '@mui/icons-material/MoveToInbox';
 import MailIcon from '@mui/icons-material/Mail';
+import { actionSetSidebar } from '../reducers/frontEndReducer';
+import { connect } from 'react-redux';
 /*
                 <List>
                     {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
@@ -44,13 +46,13 @@ import MailIcon from '@mui/icons-material/Mail';
                     ))}
                 </List>
 */
-export function Sidebar(props) {
-    let MenuComponent = props.menuComponent;
-    let drawerWidth = props.drawerWidth || 100;
+function Sidebar(props) {
+    let {drawerWidth, menuComponent, opened, openSidebar} = props;
+    let MenuComponent = menuComponent;
+    drawerWidth = drawerWidth || 100;
     const theme = useTheme();
-    const [open, setOpen] = useState(false);
     const handleDrawerClose = () => {
-        setOpen(false);
+        openSidebar(false);
     };
     const DrawerHeader = styled('div')(({ theme }) => ({
         display: 'flex',
@@ -73,7 +75,7 @@ export function Sidebar(props) {
                 }}
                 variant="persistent"
                 anchor="left"
-                open={open}
+                open={opened}
             >
                 <DrawerHeader>
                     <IconButton onClick={handleDrawerClose}>
@@ -87,3 +89,4 @@ export function Sidebar(props) {
         </>);
 }
 
+export const CSidebar = connect(state => ({ opened: state.frontend.sidebar.opened }), { openSidebar: actionSetSidebar })(Sidebar);

+ 1 - 1
src/Components/index.js

@@ -8,4 +8,4 @@ export { Order, exampleOrder } from './Order';
 export { OrderList, exampleOrderList } from './OrderList';
 export { MyLink } from './MyLink';
 export { CLogout } from './Logout';
-export { MainAppBar } from './MainAppBar';
+export { CMainAppBar } from './MainAppBar';

+ 1 - 0
src/index.js

@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client';
 import './index.css';
 import App from './App.js';
 import reportWebVitals from './reportWebVitals';
+import { App2 } from './App2';
 
 const root = ReactDOM.createRoot(document.getElementById('root'));
 

+ 1 - 1
src/reducers/authReducer.js

@@ -14,7 +14,7 @@ export function authReducer(state = {}, action) {                   // дисп
                 localStorage.authToken = newState.token;
             else
                 delete localStorage.authToken;
-            setTimeout(() => history.push('/'), 100);
+            history.push('/');
             return newState;
         }
         else if (action.type === 'AUTH_LOGOUT') {

+ 9 - 0
src/reducers/frontEndReducer.js

@@ -0,0 +1,9 @@
+export function frontEndReducer(state = { sidebar: {} }, action) {                   // диспетчер обработки login
+    if (action) {
+        if (action.type === 'SET_SIDE_BAR') {
+            return { ...state, sidebar: { opened: action.open } };
+        }
+    }
+    return state;
+}
+export const actionSetSidebar = open => ({ type: 'SET_SIDE_BAR', open });

+ 2 - 1
src/reducers/index.js

@@ -1,4 +1,5 @@
 export { promiseReducer, actionPromise, actionFulfilled, actionPending, actionRejected } from "./promiseReducer";
 export { authReducer, actionAuthLogin, actionAuthLogout, actionAuthLoginThunk } from "./authReducer";
 export { cartReducer, actionCartAdd, actionCartClear, actionCartDel, actionCartSet, actionCartShow, actionCartSub } from "./cartReducer";
-export { localStoredReducer, } from "./localStoredReducer";
+export { localStoredReducer, } from "./localStoredReducer";
+export { frontEndReducer, } from "./frontEndReducer";