123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- import "./App.scss";
- import {createStore, combineReducers, applyMiddleware} from "redux";
- import {Provider, connect} from "react-redux";
- import thunk from "redux-thunk";
- import React, {useState, useEffect} from "react";
- import {Router, Route, Link, Redirect, Switch} from 'react-router-dom';
- import createHistory from "history/createBrowserHistory";
- const promiseReducer = function(state={}, {type, name, status, payload, error}) {
- if (type == 'PROMISE'){
- return {
- ...state,
- [name]:{status, payload, error}
- }
- }
- return state;
- };
- const actionPending = name => ({type: "PROMISE", name, status: 'PENDING'});
- const actionFulfilled = (name,payload) => ({type: "PROMISE", name, status: 'FULFILLED', payload});
- const actionRejected = (name,error) => ({type: "PROMISE", name, status: 'REJECTED', error});
- const actionPromise = function(name, promise) {
- return async dispatch => {
- dispatch(actionPending(name));
- try {
- let payload = await promise
- dispatch(actionFulfilled(name, payload))
- return payload
- }
- catch(error){
- dispatch(actionRejected(name, error))
- };
- };
- };
- let jwtDecode = function(token) {
- let payloadInBase64;
- let payloadInJson;
- let payload;
- try {
- payloadInBase64 = token.split(".")[1];
- payloadInJson = atob(payloadInBase64);
- payload = JSON.parse(payloadInJson);
- return payload;
- }
- catch(err) {
- }
- };
- const authReducer = function(state, {type, token}) {
- let payload;
- if (state == undefined) {
- if(localStorage.authToken) {
- type = "AUTH_LOGIN";
- token = localStorage.authToken;
- } else {
- type = "AUTH_LOGOUT";
- };
- };
- if (type == "AUTH_LOGIN") {
- payload = jwtDecode(token);
- if(payload) {
- localStorage.authToken = token;
- return {
- token: token,
- payload: payload
- }
- }
- };
- if (type == "AUTH_LOGOUT") {
- localStorage.removeItem("authToken");
- return {};
- };
- return state || {};
- };
- const actionAuthLogin = token => ({type: "AUTH_LOGIN", token});
- const actionAuthLogout = () => ({type: "AUTH_LOGOUT"});
- const actionFullLogin = function(login, password) {
- return async dispatch => {
- let token = await gql("query userLogin($login: String, $password: String) {login(login: $login, password: $password)}", {"login": login, "password": password});
- dispatch(actionAuthLogin(token));
- };
- };
- let actionFullRegister = function(login, password, nick) {
- return async dispatch => {
- dispatch(actionPromise("userRegister", gql(`mutation userRegister($login:String, $password:String, $nick:String) {
- UserUpsert(user: {login:$login, password:$password, nick:$nick}) {
- _id login nick
- }
- }`,
- {
- "login": login,
- "password": password,
- "nick": nick
- })));
- dispatch(actionFullLogin(login, password));
- };
- };
- let cartReducer = function(state={}, {type, good, count=1}) {
- if(type == "CART_ADD") {
- let newState = {...state};
- if(good["_id"] in state) {
- newState[good._id] = {count: newState[good._id].count + count, good}
- }
- else {
- newState = {
- ...state,
- [good._id]: {count, good}
- };
- };
- return newState;
- };
- if(type == "CART_CHANGE") {
- let newState = {...state,
- [good._id]: {count, good}
- };
- return newState;
- };
- if(type == "CART_DELETE") {
- let newState = {...state};
- delete newState[good._id];
- return newState;
- };
- if(type == "CART_CLEAR") {
- return {};
- };
- return state;
- };
- const actionCartAdd = (good, count=1) => ({type: 'CART_ADD', good, count: +count})
- const actionCartChange = (good, count=1) => ({type: 'CART_CHANGE', good, count: +count})
- const actionCartDelete = (good) => ({type: 'CART_DELETE', good})
- const actionCartClear = () => ({type: 'CART_CLEAR'})
- const getGQL = function(url) {
- return async function(query, variables) {
- const res = await fetch(url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- ...(localStorage.authToken ? { "Authorization": "Bearer " + localStorage.authToken } : {})
- },
- body: JSON.stringify({ query, variables })
- });
- const data = await res.json();
- if (data.data) {
- return Object.values(data.data)[0];
- }
- else {
- throw new Error(JSON.stringify(data.errors));
- };
- };
- };
- const backendURL = 'http://shop-roles.asmer.fs.a-level.com.ua';
- const gql = getGQL(backendURL + '/graphql');
- let actionRootCats = function() {
- return actionPromise("rootCats", gql(`query {
- CategoryFind(query: "[{\\"parent\\":null}]"){
- _id name
- }
- }`));
- };
- let actionCatById = function(_id) {
- return actionPromise("catById", gql(`query catById($q: String){
- CategoryFindOne(query: $q){
- _id name goods {
- _id name price images {
- url
- }
- }
- }
- }`,
- {q: JSON.stringify([{_id}])}
- ));
- };
- let actionGoodById = function(_id) {
- return actionPromise("goodById", gql(`query findGood($goodQuery: String) {
- GoodFindOne(query:$goodQuery) {
- _id name price images {
- url
- }
- }
- }`,
- {goodQuery: JSON.stringify([{"_id": _id}])}
- ));
- };
- let actionOrders = async function() {
- let order = await gql(`mutation makeOrder($order:OrderInput){
- OrderUpsert(order: $order){
- _id
- }
- }`, {
- "order": {
- orderGoods: Object.entries(store.getState().cart).map(([_id, count]) =>({"count": count.count, "good": {_id}}))
- }
- });
- store.dispatch(actionCartClear());
- }
- let actionOrdersFind = function() {
- return actionPromise("ordersFind", gql(`query ordersFind($query:String) {
- OrderFind(query: $query) {
- createdAt orderGoods {
- count good {
- name price images {
- url
- }
- }
- }
- }
- }`,
- {
- "query": JSON.stringify([{}])
- }));
- }
- const store = createStore(combineReducers({promise: promiseReducer,
- auth: authReducer,
- cart: cartReducer}), applyMiddleware(thunk));
- store.subscribe(() => console.log(store.getState()));
- store.dispatch(actionRootCats());
- let Nav = function({auth}) {
- return (
- <ul className="nav">
- <li>
- <Link to="/login">Логин</Link>
- </li>
- <li>
- <Link to="/registration">Регистрация</Link>
- </li>
- <li>
- <Link to="/cart">Корзина</Link>
- </li>
- <li>
- <Link to="/dashboard">История покупок</Link>
- </li>
- </ul>
- );
- };
- const CNav = connect(state => ({auth: state.auth}))(Nav);
- let Header = function() {
- return (
- <header className="header">
- <div className="header__logo">Типо логотип</div>
- <CNav />
- </header>
- );
- };
- let RootCategories = function({rootCats}) {
- return (
- <aside className="rootCats">
- <ul className="rootCats__list">
- {rootCats.map(rootCat => <li><Link to={`/category/${rootCat._id}`}>{rootCat.name}</Link></li>)}
- </ul>
- </aside>
- );
- };
- const CRootCategories = connect(state => ({rootCats: state.promise?.rootCats?.payload || []}))(RootCategories);
- let MainPage = function() {
- return (
- <h1>Главная страничка</h1>
- );
- };
- let GoodCard = function({good}) {
- return (
- <li className="goods__item">
- <img src={`${backendURL}/${good.images[0].url}`} />
- <b>{good.name}</b><br />
- <span>Цена: {good.price}</span><br />
- <Link to={`/good/${good._id}`}>Перейти на страничку товара</Link>
- </li>
- );
- };
- let Goods = function({goods}) {
- return (
- <section className="goods">
- <ul className="goods__list">
- {goods.map(good => <GoodCard good={good} />)}
- </ul>
- </section>
- );
- };
- const CGoods = connect(state => ({goods: state.promise?.catById?.payload?.goods || []}))(Goods);
- let Categories = function({match: {params: {_id}}, catById}) {
- catById(_id);
- return (
- <CGoods />
- );
- };
- const CCategories = connect(null, {catById: actionCatById})(Categories);
- let GoodPageCard = function({good, cartAdd}) {
- return (
- <section className="good">
- <img src={`${backendURL}/${good?.images[0]?.url}`} />
- <div>
- <b>{good?.name}</b><br />
- <span>Цена: {good?.price}</span><br />
- <button onClick={() => cartAdd(good)}>Добавить в корзину</button>
- </div>
- </section>
- );
- };
- const CGoodPageCard = connect(state => ({good: state.promise?.goodById?.payload}),
- {cartAdd: actionCartAdd})(GoodPageCard)
- let GoodPage = function({match: {params: {_id}}, goodById}) {
- goodById(_id);
- return (
- <CGoodPageCard />
- );
- };
- const CGoodPage = connect(null, {goodById: actionGoodById})(GoodPage);
- let Login = function({fullLogin}) {
- const [login, setLogin] = useState("");
- const [password, setPassword] = useState("");
- return (
- <section className="login">
- <h2>Логин</h2>
- <input type="text"
- placeholder="Введите ваш логин"
- onChange={evt => setLogin(evt.target.value)} /><br />
- <input type="password"
- placeholder="Введите ваш пароль"
- onChange={evt => setPassword(evt.target.value)} /><br />
- <button onClick={() => fullLogin(login, password)}>Залогинится</button>
- </section>
- );
- };
- const CLogin = connect(null, {fullLogin: actionFullLogin})(Login);
- let Registration = function({fullRegister}) {
- const [login, setLogin] = useState("");
- const [nick, setNick] = useState("");
- const [password, setPassword] = useState("");
- return (
- <section className="registration">
- <h2>Регистрация</h2>
- <input type="text"
- placeholder="Введите ваш логин"
- onChange={evt => setLogin(evt.target.value)} /><br/>
- <input type="text"
- placeholder="Введите ваш никнейм"
- onChange={evt => setNick(evt.target.value)} /><br/>
- <input type="password"
- placeholder="Введите ваш пароль"
- onChange={evt => setPassword(evt.target.value)} /><br/>
- <button onClick={() => fullRegister(login, password, nick)}>Зарегистрироваться</button>
- </section>
- );
- };
- const CRegistration = connect(null, {fullRegister: actionFullRegister})(Registration)
- let CardGood = function({good: {count, good}, cartChange, cartDelete}) {
- const [value, setValue] = useState(count);
- return (
- <tr>
- <td>
- <img className="cart__img"
- src={`${backendURL}/${good?.images[0]?.url}`} />
- </td>
- <td>{good?.name}</td>
- <td>Цена: {good?.price}</td>
- <td>Количество:
- <input type="number"
- value={value}
- onChange={evt => {
- setValue(evt.target.value);
- cartChange(good, evt.target.value);
- }}/>
- </td>
- <td>
- <button onClick={() => cartDelete(good)}>Удоли</button>
- </td>
- </tr>
- );
- };
- const CCardGood = connect(null, {cartChange: actionCartChange,
- cartDelete: actionCartDelete})(CardGood);
- let Cart = function({cart, makeOrder}) {
- return (
- <section className="cart">
- <h2>Корзина</h2>
- <table>
- {cart.map(good => <CCardGood good={good} />)}
- </table>
- <button onClick={() => makeOrder()}>Сделать заказ</button>
- </section>
- );
- };
- const CCart = connect(state => ({cart: Object.values(state.cart) || []}),
- {makeOrder: actionOrders})(Cart);
- let OrderGood = function({good: {count, good}}) {
- return (
- <li>
- <ul className="dashboard__good-list">
- <li>
- <img className="dashboard__img"
- src={`${backendURL}/${good?.images[0]?.url}`} />
- </li>
- <li>{good?.name}</li>
- <li>Цена: {good?.price}</li>
- <li>Количество: {count}</li>
- </ul>
- </li>
- );
- };
- let Order = function({order: {createdAt, orderGoods}}) {
- return (
- <tr>
- <td className="dashboard__date">{(new Date(+createdAt)).toLocaleString()}</td>
- <td>
- <ul className="dashboard__list">
- {orderGoods.map(orderGood => <OrderGood good={orderGood} />)}
- </ul>
- </td>
- </tr>
- );
- }
- let Dashboard = function({orders}) {
- return (
- <table>
- {orders.map(order => <Order order={order} />)}
- </table>
- );
- };
- const CDashboardTable = connect(state => ({orders: state.promise?.ordersFind?.payload || []}))(Dashboard)
- let PreDashboard = function({orderFind}) {
- orderFind();
- return (
- <section className="dashboard">
- <h2>История заказов</h2>
- <CDashboardTable />
- </section>
- );
- };
- const CDashboard = connect(null, {orderFind: actionOrdersFind})(PreDashboard);
- let Content = function() {
- return (
- <Switch>
- <Route path="/" exact component={MainPage} />
- <Route path="/category/:_id" exact component={CCategories} />
- <Route path="/good/:_id" exact component={CGoodPage} />
- <Route path="/login" exact component={CLogin} />
- <Route path="/registration" exact component={CRegistration} />
- <Route path="/cart" exact component={CCart} />
- <Route path="/dashboard" exact component={CDashboard} />
- </Switch>
- );
- };
- let Main = function() {
- return (
- <main className="main">
- <CRootCategories />
- <Content />
- </main>
- );
- };
- const history = createHistory()
- function App() {
- return (
- <Router history={history}>
- <Provider store={store}>
- <div className="App">
- <Header />
- <Main />
- </div>
- </Provider>
- </Router>
- );
- }
- export default App;
|