App.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. import logoDefault from './logo.svg';
  2. import './App.scss';
  3. import {Provider, connect} from 'react-redux';
  4. import {createStore, combineReducers, applyMiddleware} from 'redux';
  5. import thunk from 'redux-thunk'
  6. function authReducer(state, { type, token }) {
  7. if (!state) {
  8. if (localStorage.authToken) {
  9. type = 'AUTH_LOGIN'
  10. token = localStorage.authToken
  11. } else state = {}
  12. }
  13. if (type === 'AUTH_LOGIN') {
  14. localStorage.setItem('authToken', token)
  15. let payload = jwtDecode(token)
  16. if (typeof payload === 'object') {
  17. return {
  18. ...state,
  19. token,
  20. payload
  21. }
  22. } else return state
  23. }
  24. if (type === 'AUTH_LOGOUT') {
  25. localStorage.removeItem('authToken')
  26. return {}
  27. }
  28. return state
  29. }
  30. const jwtDecode = token => {
  31. try {
  32. let arrToken = token.split('.')
  33. let base64Token = atob(arrToken[1])
  34. return JSON.parse(base64Token)
  35. }
  36. catch (e) {
  37. console.log('Лажа, Бро ' + e);
  38. }
  39. }
  40. const actionAuthLogin = token => ({ type: 'AUTH_LOGIN', token })
  41. const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' })
  42. const actionLogin = (login, password) =>
  43. actionPromise('login', gql(`query NameForMe1($login:String, $password:String){
  44. login(login:$login, password:$password)
  45. }`, { login, password }))
  46. function cartReducer(state = {}, { type, good = {}, count = 1 }) {
  47. const { _id } = good
  48. const types = {
  49. CART_ADD() {
  50. count = +count
  51. if (!count) return state
  52. return {
  53. ...state,
  54. [_id]: {
  55. good,
  56. count: count + (state[_id]?.count || 0)
  57. }
  58. }
  59. },
  60. CART_CHANGE() {
  61. count = +count
  62. if (!count) return state
  63. return {
  64. ...state,
  65. [_id]: {
  66. good,
  67. count: count
  68. }
  69. }
  70. },
  71. CART_REMOVE() {
  72. let { [_id]: remove, ...newState } = state
  73. return {
  74. ...newState
  75. }
  76. },
  77. CART_CLEAR() {
  78. return {}
  79. },
  80. }
  81. if (type in types) {
  82. return types[type]()
  83. }
  84. return state
  85. }
  86. const actionCartAdd = (good, count) => ({type: 'CART_ADD', good, count})
  87. const actionCartChange = (good, count) => ({type: 'CART_CHANGE', good, count})
  88. const actionCartRemove = (good) => ({type: 'CART_REMOVE', good})
  89. const actionCartClear = () => ({type: 'CART_CLEAR'})
  90. function promiseReducer(state = {}, { type, status, payload, error, name }) {
  91. if (type === 'PROMISE') {
  92. return {
  93. ...state,
  94. [name]: { status, payload, error }
  95. }
  96. }
  97. return state;
  98. }
  99. const actionPending = name => ({ type: 'PROMISE', status: 'PENDING', name })
  100. const actionResolved = (name, payload) => ({ type: 'PROMISE', status: 'RESOLVED', name, payload })
  101. const actionRejected = (name, error) => ({ type: 'PROMISE', status: 'REJECTED', name, error })
  102. const actionPromise = (name, promise) => (
  103. async dispatch => {
  104. dispatch(actionPending(name))
  105. try {
  106. let data = await promise
  107. dispatch(actionResolved(name, data))
  108. return data
  109. }
  110. catch (error) {
  111. dispatch(actionRejected(name, error))
  112. }
  113. }
  114. )
  115. const actionRootCats = () => (
  116. actionPromise('rootCats', gql(`query {
  117. CategoryFind(query: "[{\\"parent\\":null}]"){
  118. _id name
  119. }
  120. }`))
  121. )
  122. const actionCatById = (_id) => (
  123. actionPromise('catById', gql(`query catById($q: String){
  124. CategoryFindOne(query: $q){
  125. subCategories{name, _id}
  126. _id name goods {
  127. _id name price images {
  128. url
  129. }
  130. }
  131. }
  132. }`, { q: JSON.stringify([{ _id }]) }))
  133. )
  134. const getGQL = url => (
  135. async (query, variables = {}) => {
  136. let obj = await fetch(url, {
  137. method: 'POST',
  138. headers: {
  139. "Content-Type": "application/json",
  140. Authorization: localStorage.authToken ? 'Bearer ' + localStorage.authToken : {},
  141. },
  142. body: JSON.stringify({ query, variables })
  143. })
  144. let a = await obj.json()
  145. if (!a.data && a.errors)
  146. throw new Error(JSON.stringify(a.errors))
  147. return a.data[Object.keys(a.data)[0]]
  148. }
  149. )
  150. const backURL = 'http://shop-roles.asmer.fs.a-level.com.ua'
  151. const gql = getGQL(backURL + '/graphql');
  152. const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer}),
  153. applyMiddleware(thunk))
  154. store.subscribe(() => console.log(store.getState()))
  155. store.dispatch(actionRootCats())
  156. store.dispatch(actionCatById('5dc49f4d5df9d670df48cc64'))
  157. const Logo = ({logo}) => (
  158. <a href='#' className="Logo">
  159. <img src={logo} />
  160. </a>
  161. )
  162. const Koshik = ({cart}) => {
  163. let count = 0;
  164. console.log(cart)
  165. let sum = Object.entries(cart).map(([, val]) => val.count)
  166. count = sum.reduce((a, b) => a + b, 0)
  167. return (
  168. <div className='Koshik'>{count}</div>
  169. )
  170. }
  171. const CKoshik = connect(({cart}) => ({cart}))(Koshik)
  172. const Header = ({logo=logoDefault}) => (
  173. <header>
  174. <CKoshik />
  175. <Logo logo={logo} />
  176. </header>
  177. )
  178. const defaultRootCats = [
  179. {_id: '5dc49f4d5df9d670df48cc64', name: 'Airconditions'},
  180. {_id: '5dc458985df9d670df48cc47', name: ' Smartphones'},
  181. {_id: '5dc4b2553f23b553bf354101', name: 'Крупная бытовая техника'},
  182. {_id: '5dcac1b56d09c45440d14cf8', name: 'Макароны'},
  183. {_id: '5dcac6cf6d09c45440d14cfd', name: 'Drinks'},
  184. {_id: '5dcacaeb6d09c45440d14d04', name: 'Салаты'},
  185. {_id: '61715b92ef4e1b3e3b67703c', name: 'Приятный бонус'},
  186. ]
  187. const RootCategory = ({cat:{_id, name}={}}) => (
  188. <li>
  189. <a href={`#/category/${_id}`}>{name}</a>
  190. </li>
  191. )
  192. const RootCategories = ({cats=defaultRootCats}) => (
  193. <ul className='RootCategories'>
  194. {cats.map(cat => <RootCategory cat={cat}/>)}
  195. </ul>
  196. )
  197. const CRootCategories = connect(state => ({cats: state.promise.rootCats?.payload || []}))(RootCategories)
  198. const Aside = ({}) => (
  199. <aside>
  200. <CRootCategories/>
  201. </aside>
  202. )
  203. const Content = ({children}) => (
  204. <div className='Content'>
  205. {children}
  206. </div>
  207. )
  208. const SubCategories = ({cats}) => (
  209. <></>
  210. )
  211. const GoodCard = ({good:{_id, name, price, images}={}, onCartAdd}) =>
  212. <div className='GoodCard'>
  213. <h2>{name}</h2>
  214. {images && images[0] && images[0].url && <img src={backURL + '/' + images[0].url} />}
  215. <strong>{price}</strong>
  216. <button onClick={() => onCartAdd({_id, name, price, images})}>+</button>
  217. </div>
  218. const CGoodCard = connect(null, {onCartAdd: actionCartAdd})(GoodCard)
  219. const Category = ({cat:{_id, name, goods, subCategories}}) => (
  220. <div className="Category">
  221. <h1>{name}</h1>
  222. {subCategories && <SubCategories cats={subCategories}/>}
  223. {(goods || []).map(good => <CGoodCard good={good}/>)}
  224. </div>
  225. )
  226. const CCategory = connect(state => ({cat: state.promise.catById?.payload || []}))(Category)
  227. const Main = ({}) => (
  228. <main>
  229. <Aside />
  230. <Content>
  231. <CCategory/>
  232. </Content>
  233. </main>
  234. )
  235. const Footer = ({logo=logoDefault}) => (
  236. <footer>
  237. <Logo logo={logo} />
  238. </footer>
  239. )
  240. const JSONTest = ({data}) => (
  241. <pre>
  242. {JSON.stringify(data, null, 4)}
  243. {Math.random() > 0.5 ? <h1>qqqqqq</h1> : <h2>qq</h2>}
  244. {Math.random() > 0.5 && <h1>zzzz</h1>}
  245. </pre>
  246. )
  247. const ReduxJSON = connect(state => ({data: state}))(JSONTest)
  248. const ListItem = ({item}) => (
  249. <li>{item}</li>
  250. )
  251. const List = ({data=["пиво", "чипсы", "сиги",]}) => (
  252. <ul>
  253. {/* {[ <ListItem item={data[0]}/>,
  254. <ListItem item={data[1]}/>,
  255. <ListItem item={data[2]}/>
  256. ]} */}
  257. {data.map(item => <ListItem item={item}/>)}
  258. </ul>
  259. )
  260. function App() {
  261. return (
  262. <Provider store={store}>
  263. <div className="App">
  264. <Header />
  265. <Main />
  266. {/* <List /> */}
  267. {/* <JSONTest data={[1,2,{a:5}]} /> */}
  268. {/* <ReduxJSON /> */}
  269. <Footer />
  270. </div>
  271. </Provider>
  272. );
  273. }
  274. export default App;