123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- import { useEffect, useState } from 'react';
- 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';
- import { Link, Route, Router, Switch, Redirect } from 'react-router-dom';
- import createHistory from 'history/createBrowserHistory'
- 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": "application/json",
- ...(localStorage.authToken ? { "Authorization": "Bearer " + localStorage.authToken } :
- {})
- },
- 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;
- }
- }
- 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
- }
- 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 = () =>
- <Link to="/">
- <img src={logo} className="Logo" alt="logo" />
- </Link>
- const Header = () =>
- <header>
- <Logo />
- </header>
- const Navbar = () =>
- <nav className='Navbar'>
- <CKoshik/>
- <LogBtn />
- </nav>
- const CategoryListItem = ({_id, name}) =>
- <li className='CatLink'>
- <Link to={`/category/:${_id}`}>{name}</Link>
- </li>
- const CategoryList = ({cats}) =>
- <ul>{cats.map((item) => <CategoryListItem {...item}/>)}</ul>
- const CCategoryList = connect(state => ({cats:state.promise.rootCats?.payload || []}))(CategoryList)
- const Aside = () =>
- <aside><CCategoryList /></aside>
- const GoodCard = ({good:{_id, name, price, images}, onAdd}) =>
- <li className='GoodCard'>
- <h2>{name}</h2>
- {images && images[0] && images[0].url && <img className='GoodImg' alt='img' src={backendURL + '/' + images[0].url} />}
- <br/>
- <button>На страницу товара</button>
- <br/>
- <strong>Цена: {price}</strong>
- <br/>
- <button onClick={() => onAdd({_id, name, price, images})}>Добавить в корзину</button>
- </li>
- const CGoodCard = connect(null, {onAdd: actionCartAdd})(GoodCard)
- const LogBtn = () =>
- <div className='KoshikCnt item'>
- <Link to="/login" style={{color:'white', textDecoration:'none'}}>Войти</Link>
- </div>
- const Koshik = ({cart}) => {
- let goodsInCart = cart
- let allGoodsInCart = 0
- for (let key in goodsInCart) {
- allGoodsInCart += goodsInCart[key].count
- }
- return (
- <div className='KoshikCnt item'>
- <Link to="/cart" style={{color:'white', textDecoration:'none'}}>Корзина: {allGoodsInCart}</Link>
- </div>
- )
- }
- const CKoshik = connect(({cart}) => ({cart}))(Koshik)
- const Category = ({cat:{name, goods=[]}={}}) =>
- <div className='Category'>
- <h1>{name}</h1>
- <ul>
- {(goods || []).map(good => <CGoodCard good={good} />)}
- </ul>
- </div>
- const CCategory = connect(state => ({cat:state.promise.catById?.payload || {}}))
- (Category)
- const CartItem = ({cart:{_id, name, price, images}, count: {count}, onChange, onRemove}) => {
- console.log('good', _id)
- return(
- <li className='GoodCard'>
- <h2>{name}</h2>
- {images && images[0] && images[0].url && <img className='GoodImg' alt='img' src={backendURL + '/' + images[0].url} />}
- <br/>
- <strong>Цена: {price * count}</strong>
- <br/>
- <label>Кол-во покупки: <input type="number" value={count} min="1" onInput={(e) => onChange({_id, name, price, images}, e.target.value)}/></label>
- <br/>
- <button>Заказать</button>
- <button onClick={() => onRemove({_id, name, price, images})}>Удалить заказ[X]</button>
- </li>
- )
- }
- const CCartItem = connect(null, {onChange: actionCartChange, onRemove: actionCartRemove})(CartItem)
- const Cart = ({cart}) => {
- let cartArr = []
- for(let item in cart) {
- cartArr.push(cart[item])
- }
- console.log('cartarr',cartArr)
-
- return(
- <div>
- <h1 style={{marginLeft:'30px'}}>Корзина</h1>
- <ul>{cartArr.map(item => <CCartItem cart={item.good} count={item} />)}</ul>
- </div>
- )
- }
- const CCart = connect(state => ({cart:state.cart}))(Cart)
- const PageCart = ({match: {params: {_id}}, getData}) => {
- useEffect(() => {
- getData(_id.substring(1))
- console.log('get', _id,typeof _id)
- },[_id])
- return (
- <CCart />
- )
- }
- const CPageCart= connect(null, {getData: actionCatById})(PageCart)
- //const CCart = connect(забрать из редакса корзину положить в пропс cart,
- //дать компоненту onCartChange и onCartRemove с соответствующими actionCreator)(Cart)
- const LoginForm = ({onLogin}) => {
- let [pass, setPass] = useState()
- let [login, setLogin] = useState()
- return (
- <div className='form'>
- <h3>Login Form</h3>
- <input placeholder='login' style={{outlineColor: login? 'black' : 'firebrick'}} onChange={(e) => setLogin(e.target.value)}/>
- <br/>
- <input placeholder='password' type="password" style={{outlineColor: pass? 'black' : 'firebrick'}} onChange={(e) => setPass(e.target.value)}/>
- <br/>
- <button disabled={!pass || !login} onClick={() => onLogin(login, pass)}>Login</button>
- </div>
- )
- }
- const CLoginForm = connect(null, {onLogin: actionFullLogin})(LoginForm)
- const PageMain = () => <h1>MAIN PAGE</h1>
- const PageCategory = ({match: {params: {_id}}, getData}) => {
- useEffect(() => {
- getData(_id.substring(1))
- console.log('get', _id,typeof _id)
- },[_id])
- return (
- <CCategory />
- )
- }
- const CPageCategory = connect(null, {getData: actionCatById})(PageCategory)
- const Page404 = () => <h1> 404 </h1>
- const Main = () =>
- <main>
- <Aside />
- <Content>
- <Switch>
- <Redirect from='/main' to='/' />
- <Route path="/" component={PageMain} exact/>
- <Route path="/category/:_id" component={CPageCategory}/>
- <Route path="/cart" component={CCart} />
- <Route path="*" component={Page404} />
-
- {/* <CCategory /> */}
- {/* <LoginForm onLogin={(l, p) => actionFullLogin(l, p)}/> */}
- {/* <CLoginForm /> */}
- {/* <CCart /> */}
- </Switch>
- </Content>
- </main>
- const Content = ({children}) =>
- <div className="Content">{children}</div>
- const Footer = () =>
- <footer><Logo /></footer>
- const history = createHistory()
- function App() {
- return (
- <Router history={history}>
- <Provider store={store}>
- <div className="App">
- <Header />
- <Navbar />
- <Main />
- <Footer />
- {/* <CCart /> */}
- </div>
- </Provider>
- </Router>
- );
- }
- export default App;
|