App.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. import React, {useState} from 'react';
  2. import {Header} from "./components/Header";
  3. import {LoginForm} from "./components/LoginForm";
  4. import {Main} from "./components/Main";
  5. import {Input} from "./components/Input";
  6. import CategoryMenu from "./components/CategoryMenu";
  7. import Category from "./components/Category";
  8. import Footer from "./components/Footer";
  9. import thunk from "redux-thunk";
  10. import { createStore, combineReducers, applyMiddleware } from "redux";
  11. import { Provider, connect } from "react-redux";
  12. import {mapStateToPropsFactory} from "react-redux/es/connect/mapStateToProps";
  13. import Spoiler from "./components/Spoiler";
  14. import RangeInput from "./components/RangeInput";
  15. import PasswordConfirm from "./components/PasswordConfirm";
  16. function jwtDecode(token){
  17. try {
  18. return JSON.parse(atob(token.split('.')[1]))
  19. }
  20. catch(e){
  21. }
  22. }
  23. function authReducer(state, {type, token}) {
  24. if (!state) {
  25. if (localStorage.authToken) {
  26. token = localStorage.authToken
  27. type = 'AUTH_LOGIN'
  28. } else {
  29. return {}
  30. }
  31. }
  32. if (type === 'AUTH_LOGIN') {
  33. let payload = jwtDecode(token)
  34. if (typeof payload === 'object') {
  35. localStorage.authToken = token
  36. return {
  37. ...state,
  38. token,
  39. payload
  40. }
  41. } else {
  42. return state
  43. }
  44. }
  45. if (type === 'AUTH_LOGOUT') {
  46. localStorage.removeItem("authToken");
  47. return {}
  48. }
  49. return state
  50. }
  51. const actionAuthLogin = (token) => ({type: 'AUTH_LOGIN', token})
  52. const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'})
  53. function cartReducer (state={}, {type, good={}, count=1}) {
  54. if (Object.keys(state).length === 0 && localStorage.cart) {
  55. let currCart = JSON.parse(localStorage.cart)
  56. if (currCart && Object.keys(currCart).length !== 0) {
  57. state = currCart
  58. }
  59. }
  60. const {_id} = good
  61. const types = {
  62. CART_ADD() {
  63. count = +count
  64. if (!count) {
  65. return state
  66. }
  67. let newState = {
  68. ...state,
  69. [_id]: {good, count: (count + (state[_id]?.count || 0)) < 1 ? 1 : count + (state[_id]?.count || 0)}
  70. }
  71. localStorage.cart = JSON.stringify(newState)
  72. return newState
  73. },
  74. CART_CHANGE() {
  75. count = +count
  76. if (!count) {
  77. return state
  78. }
  79. let newState = {
  80. ...state,
  81. [_id]: {good, count: count < 0 ? 0 : count}
  82. }
  83. localStorage.cart = JSON.stringify(newState)
  84. return newState
  85. },
  86. CART_REMOVE() {
  87. let { [_id]: removed, ...newState } = state
  88. localStorage.cart = JSON.stringify(newState)
  89. return newState
  90. },
  91. CART_CLEAR() {
  92. localStorage.cart = JSON.stringify({})
  93. return {}
  94. },
  95. }
  96. if (type in types) {
  97. return types[type]()
  98. }
  99. return state
  100. }
  101. const actionCartAdd = (good, count) => ({type: 'CART_ADD', good, count})
  102. const actionCartChange = (good, count) => ({type: 'CART_CHANGE', good, count})
  103. const actionCartRemove = (good) => ({type: 'CART_REMOVE', good})
  104. const actionCartClear = () => ({type: 'CART_CLEAR'})
  105. function promiseReducer(state={}, {type, status, payload, error, name}) {
  106. if (!state) {
  107. return {}
  108. }
  109. if (type === 'PROMISE') {
  110. return {
  111. ...state,
  112. [name]: {
  113. status: status,
  114. payload : payload,
  115. error: error,
  116. }
  117. }
  118. }
  119. return state
  120. }
  121. const actionPending = (name) => ({type: 'PROMISE', status: 'PENDING', name})
  122. const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
  123. const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error})
  124. const actionPromise = (name, promise) => (
  125. async (dispatch) => {
  126. dispatch(actionPending(name))
  127. try {
  128. let data = await promise
  129. dispatch(actionResolved(name, data))
  130. return data
  131. }
  132. catch(error){
  133. dispatch(actionRejected(name, error))
  134. }
  135. }
  136. )
  137. const getGQL = url => (
  138. async (query, variables={}) => {
  139. let obj = await fetch(url, {
  140. method: 'POST',
  141. headers: {
  142. "Content-Type": "application/json",
  143. ...(localStorage.authToken ? {Authorization: "Bearer " + localStorage.authToken} : {})
  144. },
  145. body: JSON.stringify({ query, variables })
  146. })
  147. let a = await obj.json()
  148. if (!a.data && a.errors) {
  149. throw new Error(JSON.stringify(a.errors))
  150. } else {
  151. return a.data[Object.keys(a.data)[0]]
  152. }
  153. }
  154. )
  155. const backURL = 'http://shop-roles.node.ed.asmer.org.ua/'
  156. const gql = getGQL(backURL + 'graphql');
  157. const actionOrder = () => (
  158. async (dispatch, getState) => {
  159. let {cart} = getState()
  160. const orderGoods = Object.entries(cart)
  161. .map(([_id, {good, count}]) => ({good: {_id}, count}))
  162. let result = await dispatch(actionPromise('order', gql(`
  163. mutation newOrder($order:OrderInput){
  164. OrderUpsert(order:$order)
  165. { _id total}
  166. }
  167. `, {order: {orderGoods}})))
  168. if (result?._id) {
  169. dispatch(actionCartClear())
  170. }
  171. })
  172. const actionLogin = (login, password) => (
  173. actionPromise('login', gql(`query log($login: String, $password: String) {
  174. login(login: $login, password: $password)
  175. }`, {login, password}))
  176. )
  177. const actionFullLogin = (log, pass) => async (dispatch) => {
  178. let token = await dispatch(
  179. actionPromise(
  180. "login",
  181. gql(
  182. `query login($login: String, $password: String) {
  183. login(login: $login, password: $password)
  184. }`,
  185. { login: log, password: pass }
  186. )
  187. )
  188. );
  189. if (token) {
  190. dispatch(actionAuthLogin(token));
  191. }
  192. };
  193. const actionRegister = (login, password) => (
  194. actionPromise('register', gql(`mutation reg($user:UserInput) {
  195. UserUpsert(user:$user) {
  196. _id
  197. }
  198. }
  199. `, {user: {login, password}})
  200. )
  201. )
  202. const actionFullRegister = (login, password) => (
  203. async (dispatch) => {
  204. let registerId = await dispatch(actionRegister(login, password))
  205. if (registerId) {
  206. dispatch(actionFullLogin(login, password))
  207. }
  208. }
  209. )
  210. const actionRootCats = () => (
  211. actionPromise('rootCats', gql(`query {
  212. CategoryFind(query: "[{\\"parent\\":null}]"){
  213. _id name
  214. }
  215. }`))
  216. )
  217. const actionCatById = (_id) => (
  218. actionPromise('catById', gql(`query catById($q: String){
  219. CategoryFindOne(query: $q){
  220. _id name goods {
  221. _id name price images {
  222. url
  223. }
  224. }
  225. subCategories {
  226. _id name
  227. }
  228. }
  229. }`, {q: JSON.stringify([{_id}])}))
  230. )
  231. const actionGoodById = (_id) => (
  232. actionPromise('goodById', gql(`query goodById($q: String) {
  233. GoodFindOne(query: $q) {
  234. _id name price description images {
  235. url
  236. }
  237. }
  238. }`, {q: JSON.stringify([{_id}])}))
  239. )
  240. const actionGoodsByUser = (_id) => (
  241. actionPromise('goodByUser', gql(`query oUser($query: String) {
  242. OrderFind(query:$query){
  243. _id orderGoods{
  244. price count total good{
  245. _id name categories{
  246. name
  247. }
  248. images {
  249. url
  250. }
  251. }
  252. }
  253. owner {
  254. _id login
  255. }
  256. }
  257. }`,
  258. {query: JSON.stringify([{___owner: _id}])}))
  259. )
  260. const combinedReducer = combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer})
  261. const store = createStore(combinedReducer)
  262. const CLoginForm = connect(null, {onLogin: actionLogin})(LoginForm)
  263. function App() {
  264. return (
  265. <Provider store={store}>
  266. <div className="App">
  267. {/*<Header/>*/}
  268. {/*<Input />*/}
  269. <CLoginForm/>
  270. <Spoiler header={<h1>Заголовок!!!</h1>} open={false}>
  271. Контент 1
  272. <p>
  273. лорем ипсум траливали и тп.
  274. </p>
  275. </Spoiler>
  276. <Spoiler header={<h1>Заголовок 2 !!!</h1>} open={false}>
  277. <h2>Контент 2</h2>
  278. <p>
  279. лорем ипсум траливали и тп.
  280. </p>
  281. </Spoiler>
  282. <PasswordConfirm min={5}/>
  283. <RangeInput max={3} min={1} />
  284. <div className="main">
  285. <CategoryMenu />
  286. <Category />
  287. </div>
  288. <Footer />
  289. </div>
  290. </Provider>
  291. );
  292. }
  293. export default App;