import logo from './logo.svg'; import './App.scss'; import thunk from 'redux-thunk'; import {createStore, combineReducers, applyMiddleware} from 'redux'; import {Provider, connect} from 'react-redux'; const actionPending = name => ({type: 'PROMISE', status: 'PENDING', name}); const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload}); const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error}); const actionPromise = (name, promise) => async dispatch => { dispatch(actionPending(name)); // 1. {delay1000: {status: 'PENDING'}} try { let payload = await promise; dispatch(actionResolved(name, payload)); return payload; } catch (error) { dispatch(actionRejected(name, error)); } }; const getGQL = url => (query, variables = {}) => fetch(url, { //метод method: 'POST', headers: { //заголовок content-type 'Content-Type': 'application/json', ...(localStorage.authToken ? {'Authorization': 'Bearer ' + localStorage.authToken} : {}) }, //body с ключами query и variables body: JSON.stringify({query, variables}) }) .then(res => res.json()) .then(data => { if (data.errors && !data.data) throw new Error(JSON.stringify(data.errors)); return data.data[Object.keys(data.data)[0]]; }); const backendURL = 'http://shop-roles.asmer.fs.a-level.com.ua'; const gql = getGQL(backendURL + '/graphql'); function jwtDecode(token) { try { let decoded = token.split('.'); decoded = decoded[1]; decoded = atob(decoded); decoded = JSON.parse(decoded); return decoded; } catch (e) { return; } } //скопировать //3 редьюсера //экшоны к ним // const actionRootCats = () => actionPromise('rootCats', gql(`query { CategoryFind(query: "[{\\"parent\\":null}]"){ _id name } }`)); const actionCartAdd = (good, count = 1) => ({type: 'CART_ADD', good, count}); const actionCartRemove = (good, count = 1) => ({type: 'CART_REMOVE', good, count}); const actionCartChange = (good, count = 1) => ({type: 'CART_CHANGE', good, count}); const actionCartClear = (good, count = 1) => ({type: 'CART_CLEAR', good, count}); function cartReducer(state = {}, {type, good = {}, count = 1}) { const {_id} = good; const types = { CART_ADD() { return { ...state, [_id]: {good, count: count + (state[_id]?.count || 0)} }; }, CART_REMOVE() { let newState = {...state}; delete newState[_id]; return { ...newState }; }, CART_CHANGE() { return { ...state, [_id]: {good, count} }; }, CART_CLEAR() { return {}; }, }; if (type in types) return types[type](); return state; } function authReducer(state, {type, token}) { if (!state) { if (localStorage.authToken) { type = 'AUTH_LOGIN'; token = localStorage.authToken; } else { return {}; } } if (type === 'AUTH_LOGIN') { let auth = jwtDecode(token); if (auth) { localStorage.authToken = token; return {token, payload: auth}; } } if (type === 'AUTH_LOGOUT') { localStorage.authToken = ''; return {}; } return state; } function promiseReducer(state = {}, {type, name, status, payload, error}) { if (type === 'PROMISE') { return { ...state, [name]: {status, payload, error} }; } return state; } // Actions ============================= // Логин и логаут const actionAuthLogin = (token) => ({type: 'AUTH_LOGIN', token}); const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'}); const actionLogin = (login = 'tst', password = '123') => actionPromise('login', gql(`query ($login:String, $password:String){ login(login:$login, password:$password)}`, { 'login': login, 'password': password })); const actionFullLogin = (login = 'tst', password = '123') => async dispatch => { let token = await dispatch(actionLogin(login, password)); if (token) { dispatch(actionAuthLogin(token)); } }; // Регистрация const actionRegister = (login = 'tst', password = '123') => actionPromise('login', gql(`mutation reg($login:String, $password:String) { UserUpsert(user:{login:$login, password:$password, nick:$login}){ _id login } }`, {'login': login, 'password': password})); const actionFullRegister = (login = 'tst', password = '123') => async dispatch => { await dispatch(actionRegister(login, password)); await dispatch(actionFullLogin(login, password)); }; // Корневые категории // Товары категории const actionCatById = (_id) => actionPromise('catById', gql(`query ($q: String){ CategoryFindOne(query: $q){ _id name goods { _id name price images { url } } subCategories { _id name } } }`, {q: JSON.stringify([{_id}])})); const actionGoodById = (_id) => actionPromise('goodById', gql(`query ($good:String) { GoodFindOne(query:$good) { _id name price images { url } } }`, {good: JSON.stringify([{_id}])})); const store = createStore(combineReducers( { promise: promiseReducer, auth: authReducer, cart: cartReducer }), applyMiddleware(thunk)); store.subscribe(() => console.log(store.getState())); store.dispatch(actionRootCats()); store.dispatch(actionCatById('5dc458985df9d670df48cc47')); const Logo = () => logo; const Header = () =>
; const CategoryListItem = ({_id, name}) =>
  • {name}
  • ; const CategoryList = ({cats}) =>{ return( ) } const CCategoryList = connect(state => ({cats: state.promise.rootCats?.payload || []}))(CategoryList); const Aside = () => ; const GoodCard = ({good: {_id, name, price, images}, onAdd}) =>
  • {name}

    {images && images[0] && images[0].url && } {price} грн
  • ; const CGoodCard = connect(null, {onAdd: actionCartAdd})(GoodCard); const Koshik = ({cart}) => { let goodsInCart = cart; let allGoodsInCart = 0; for (let key in goodsInCart) { allGoodsInCart += goodsInCart[key].count; } return (

    Корзина:{allGoodsInCart}

    ); }; const CKoshik = connect(({cart}) => ({cart}))(Koshik); const Category = ({cat: {name, goods = []} = {}}) => { return(

    {name}

    ) } const CCategory = connect(state => ({cat: state.promise.catById?.payload || {}}))(Category); const Cart = ({cart, onCartChange, onCartRemove}) => { const error = typeof cart === 'undefined'; return (
    {error === false && (cart.map((good) => { return (
    {good.good.name}
    ); }) ) }
    {error === false && (cart.map((good) => { return
    {good.count}
    ; }) )}
    {error === false && (cart.map((good) => { return ( ); }) ) }
    {/* {` нарисовать страницу корзины , по изменению в input onCartChange(...)}*/ } {/*по кнопке удалить */ } {/* ТУТ БУДЕТ КОРЗИНА*/ }
    ) }; const CCart = connect( state => ({cart: Object.values(state.cart) || []}), { onCartChange: actionCartChange, onCartRemove: actionCartRemove })(Cart); // забрать из редакса корзину положить в пропс cart, //дать компоненту onCartChange и onCartRemove с соответствующими actionCreator)(Cart) const Main = () =>
    ; const Content = ({children}) =>
    {children}
    ; const Footer = () => ; //import {Provider, connect} from 'react-redux'; function App() { return (
    ); } export default App; ;