123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- import logo from "./logo.svg";
- import React, { useState } from "react";
- import "./App.css";
- import thunk from "redux-thunk";
- import { createStore, combineReducers, applyMiddleware } from "redux";
- import { Provider, connect } from "react-redux";
- const store = createStore(
- combineReducers({
- auth: authReducer,
- promise: promiseReducer,
- cart: localStoreReducer(cartReducer, "cart"),
- }),
- applyMiddleware(thunk)
- );
- function jwtDecode(token) {
- try {
- return JSON.parse(atob(token.split(".")[1]));
- } catch (e) {}
- }
- const getGQL = (url) => (query, variables) =>
- fetch(url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- // 'Accept' : 'application/json',
- ...(localStorage.authToken
- ? { Authorization: "Bearer " + localStorage.authToken }
- : {}),
- },
- body: JSON.stringify({ query, variables }),
- })
- .then((res) => res.json())
- .then((data) => {
- if (data.data) {
- return Object.values(data.data)[0];
- } else throw new Error(JSON.stringify(data.errors));
- });
- const backendURL = "http://shop-roles.node.ed.asmer.org.ua";
- const gql = getGQL(backendURL + "/graphql");
- function authReducer(state, { type, token }) {
- if (state === undefined) {
- if (localStorage.authToken) {
- type = "AUTH_LOGIN";
- token = localStorage.authToken;
- }
- }
- if (type === "AUTH_LOGIN") {
- let payload = jwtDecode(token);
- if (payload) {
- localStorage.authToken = token;
- return { token, payload };
- }
- }
- if (type === "AUTH_LOGOUT") {
- localStorage.removeItem("authToken");
- return {};
- }
- return state || {};
- }
- const actionAuthLogin = (token) => ({ type: "AUTH_LOGIN", token });
- const actionAuthLogout = () => (dispatch) => {
- dispatch({ type: "AUTH_LOGOUT" });
- localStorage.removeItem("authToken");
- };
- function promiseReducer(state = {}, { type, name, status, payload, error }) {
- if (type === "PROMISE") {
- return {
- ...state,
- [name]: { status, payload, error },
- };
- }
- return state;
- }
- const actionPending = (name) => ({
- type: "PROMISE",
- status: "PENDING",
- name,
- });
- const actionFulfilled = (name, payload) => ({
- type: "PROMISE",
- status: "FULFILLED",
- name,
- payload,
- });
- const actionRejected = (name, error) => ({
- type: "PROMISE",
- status: "REJECTED",
- name,
- error,
- });
- const actionPromise = (name, promise) => async (dispatch) => {
- try {
- dispatch(actionPending(name));
- let payload = await promise;
- dispatch(actionFulfilled(name, payload));
- return payload;
- } catch (e) {
- dispatch(actionRejected(name, e));
- }
- };
- function cartReducer(state = {}, { type, count = 1, good }) {
- // type CART_ADD CART_REMOVE CART_CLEAR CART_DEL
- // {
- // id1: {count: 1, good: {name, price, images, id}}
- // }
- if (type === "CART_ADD") {
- return {
- ...state,
- [good._id]: { count: count + (state[good._id]?.count || 0), good },
- };
- }
- if (type === "CART_DELETE") {
- if (state[good._id].count > 1) {
- return {
- ...state,
- [good._id]: {
- count: -count + (state[good._id]?.count || 0),
- good,
- },
- };
- }
- if (state[good._id].count === 1) {
- let { [good._id]: id1, ...newState } = state; //o4en strashnoe koldunstvo
- //delete newState[good._id]
- return newState;
- }
- }
- if (type === "CART_CLEAR") {
- return {};
- }
- if (type === "CART_REMOVE") {
- // let newState = {...state}
- let { [good._id]: id1, ...newState } = state; //o4en strashnoe koldunstvo
- //delete newState[good._id]
- return newState;
- }
- return state;
- }
- const actionCartAdd = (good, count = 1) => ({ type: "CART_ADD", good, count });
- const actionCartDelete = (good) => ({ type: "CART_DELETE", good });
- const actionCartClear = () => ({ type: "CART_CLEAR" });
- const actionCartRemove = (good) => ({ type: "CART_REMOVE", good });
- function localStoreReducer(reducer, localStorageKey) {
- function localStoredReducer(state, action) {
- // Если state === undefined, то достать старый state из local storage
- if (state === undefined) {
- try {
- return JSON.parse(localStorage[localStorageKey]);
- } catch (e) {}
- }
- const newState = reducer(state, action);
- // Сохранить newState в local storage
- localStorage[localStorageKey] = JSON.stringify(newState);
- return newState;
- }
- return localStoredReducer;
- }
- const actionRootCats = () =>
- actionPromise(
- "rootCats",
- gql(
- `query {
-
- CategoryFind(query: "[{\\"parent\\":null}]"){
- _id name
- }
- }`
- )
- );
- const actionCatById = (_id) =>
- actionPromise(
- "catById",
- gql(
- `query catById($q: String){
- CategoryFindOne(query: $q){
- _id name subCategories {
- name _id
- }
- goods {
- _id name price images {
- url
- }
- }
- }
- }`,
- { q: JSON.stringify([{ _id }]) }
- )
- );
- const actionLogin = (login, password) =>
- actionPromise(
- "actionLogin",
- gql(
- `query log($login:String, $password:String){
- login(login:$login, password:$password)
- }`,
- { login, password }
- )
- );
- const actionGoodById = (_id) =>
- actionPromise(
- "GoodFineOne",
- gql(
- `query goodByid($goodId: String) {
- GoodFindOne(query: $goodId) {
- _id
- name
- price
- description
- images {
- url
- }
- }
- }`,
- { goodId: JSON.stringify([{ _id }]) }
- )
- );
- store.dispatch(actionRootCats());
- store.dispatch(actionCatById("6262ca7dbf8b206433f5b3d1"));
- const actionFullLogin = (log, pass) => async (dispatch) => {
- let token = await dispatch(
- actionPromise(
- "login",
- gql(
- `query login($login: String, $password: String) {
- login(login: $login, password: $password)
- }`,
- { login: log, password: pass }
- )
- )
- );
- if (token) {
- dispatch(actionAuthLogin(token));
- }
- };
- const actionFullRegister = (login, password) => async (dispatch) => {
- let user = await dispatch(
- actionPromise(
- "register",
- gql(
- `mutation register($login: String, $password: String) {
- UserUpsert(user: {login: $login, password: $password}) {
- _id
- login
- }
- }`,
- { login: login, password: password }
- )
- )
- );
- if (user) {
- dispatch(actionFullLogin(login, password));
- }
- };
- const actionOrder = () => async (dispatch, getState) => {
- let { cart } = getState();
- const orderGoods = Object.entries(cart).map(([_id, { count }]) => ({
- good: { _id },
- count,
- }));
- let result = await dispatch(
- actionPromise(
- "order",
- gql(
- `
- mutation newOrder($order:OrderInput){
- OrderUpsert(order:$order)
- { _id total }
- }
- `,
- { order: { orderGoods } }
- )
- )
- );
- if (result?._id) {
- dispatch(actionCartClear());
- document.location.hash = "#/cart/";
- alert("Purchase completed");
- }
- };
- const orderHistory = () =>
- actionPromise(
- "history",
- gql(` query OrderFind{
- OrderFind(query:"[{}]"){
- _id total createdAt orderGoods{
- count good{
- _id name price images{
- url
- }
- }
- owner{
- _id login
- }
- }
- }
- }
- `)
- );
- const LoginForm = ({ onLogin }) => {
- const [login, setLogin] = useState("");
- const [password, setPassword] = useState("");
- const checkDisable = () => {
- return !(login !== "" && password.match(/(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/));
- };
- const Authorization = () => {
- onLogin(login, password);
- };
- return (
- <div className="formBlock">
- <p>Enter login:</p>
- <input
- type="text"
- value={login}
- onChange={(e) => setLogin(e.target.value)}
- />
- <p>Enter password:</p>
- <input
- type="password"
- value={password}
- onChange={(e) => setPassword(e.target.value)}
- />
- <button disabled={checkDisable()} onClick={Authorization}>
- Log In
- </button>
- </div>
- );
- };
- if (localStorage.authToken) {
- store.dispatch(actionAuthLogin(localStorage.authToken));
- }
- store.subscribe(() => console.log(store.getState()));
- const cat = [
- {
- _id: "62d57ab8b74e1f5f2ec1a148",
- name: "Motorola Razr 5G 8/256GB Graphite",
- price: 3500,
- },
- {
- _id: "62d57c4db74e1f5f2ec1a14a",
- name: "Смартфон Google Pixel 6 Pro 12/128GB Stormy Black",
- price: 2800,
- },
- {
- _id: "62d58318b74e1f5f2ec1a14e",
- name: "Microsoft Surface Duo 2 8GB/256GB",
- price: 4500,
- },
- {
- _id: "62d5869bb74e1f5f2ec1a150",
- name: "Смартфон Poco F3 6/128GB EU Arctic White",
- price: 1800,
- },
- {
- _id: "62d58810b74e1f5f2ec1a152",
- name: "Мобильный телефон Xiaomi Redmi Note 9 4G (Redmi 9t EU)",
- price: 800,
- },
- {
- _id: "62d5a7deb74e1f5f2ec1a154",
- name: "LG V50 black REF",
- price: 900,
- },
- ];
- const rootCats = [
- {
- name: "test3",
- },
- {
- name: "Tools",
- },
- {
- name: "Tomatoes",
- },
- {
- name: "123",
- },
- {
- name: "iPhone",
- },
- {
- name: "Samsung",
- },
- {
- name: "Smartphone",
- },
- {
- name: "Large home appliances",
- },
- {
- name: "Garden",
- },
- {
- name: "Children's products",
- },
- {
- name: " Hobbies and sports",
- },
- {
- name: "Sale",
- },
- ];
- const CategoryMenu = ({rootCats = []}) => {
- return (
- <>
- <aside id="aside">
- <ul>
- {rootCats.map((cat) => (
- <CategoryMenuItem name={cat.name} />
- ))}
- </ul>
- </aside>
- </>
- );
- };
- const CCategoryMenu = connect((state) => ({rootCats: state.promise?.rootCats.payload}))(CategoryMenu)
- const CategoryMenuItem = ({ name }) => {
- return (
- <>
- <h2>{name}</h2>
- </>
- );
- };
- const Category = ({cat = []}) => {
- return (
- <div className="cardBlock">
- {cat.map((item) => (
- <GoodCard name={item.name} price={item.price} />
- ))}
- </div>
- );
- };
- const CCategory = connect((state) => ({cat: state.promise?.catById.payload?.goods}))(Category)
- const GoodCard = ({ name, price }) => {
- return (
- <div className="card">
- <h3>{name}</h3>
- <h4>{price}</h4>
- </div>
- );
- };
- const CLoginForm = connect(null, { onLogin: actionFullLogin })(LoginForm);
- function App() {
- return (
- <Provider store={store}>
- <div className="App">
- <CLoginForm />
- <CCategoryMenu />
- <CCategory />
- </div>
- </Provider>
- );
- }
- export default App;
|