123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803 |
- function createStore(reducer) {
- let state = reducer(undefined, {}); //стартовая инициализация состояния, запуск редьюсера со state === undefined
- let cbs = []; //массив подписчиков
- const getState = () => state; //функция, возвращающая переменную из замыкания
- const subscribe = (cb) => (
- cbs.push(cb), //запоминаем подписчиков в массиве
- () => (cbs = cbs.filter((c) => c !== cb))
- ); //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
- const dispatch = (action) => {
- if (typeof action === "function") {
- //если action - не объект, а функция
- return action(dispatch, getState); //запускаем эту функцию и даем ей dispatch и getState для работы
- }
- const newState = reducer(state, action); //пробуем запустить редьюсер
- if (newState !== state) {
- //проверяем, смог ли редьюсер обработать action
- state = newState; //если смог, то обновляем state
- for (let cb of cbs) cb(); //и запускаем подписчиков
- }
- };
- return {
- getState, //добавление функции getState в результирующий объект
- dispatch,
- subscribe, //добавление subscribe в объект
- };
- }
- function jwtDecode(token) {
- try {
- return JSON.parse(atob(token.split(".")[1]));
- } catch (e) {}
- }
- 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 delay = (ms) => new Promise((ok) => setTimeout(() => ok(ms), ms));
- function combineReducers(reducers) {
- //пачку редьюсеров как объект {auth: authReducer, promise: promiseReducer}
- function combinedReducer(combinedState = {}, action) {
- //combinedState - типа {auth: {...}, promise: {....}}
- const newCombinedState = {};
- for (const [reducerName, reducer] of Object.entries(reducers)) {
- const newSubState = reducer(combinedState[reducerName], action);
- if (newSubState !== combinedState[reducerName]) {
- newCombinedState[reducerName] = newSubState;
- }
- }
- if (Object.keys(newCombinedState).length === 0) {
- return combinedState;
- }
- return { ...combinedState, ...newCombinedState };
- }
- return combinedReducer; //нам возвращают один редьюсер, который имеет стейт вида {auth: {...стейт authReducer-а}, promise: {...стейт promiseReducer-а}}
- }
- 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");
- const store = createStore(
- combineReducers({
- auth: authReducer,
- promise: promiseReducer,
- cart: localStoreReducer(cartReducer, "cart"),
- })
- ); //не забудьте combineReducers если он у вас уже есть
- if (localStorage.authToken) {
- store.dispatch(actionAuthLogin(localStorage.authToken));
- }
- //const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer}))
- store.subscribe(() => console.log(store.getState()));
- // const backendURL = "http://shop-roles.node.ed.asmer.org.ua/graphql";
- // const backendURLNotGraphQL = "http://shop-roles.node.ed.asmer.org.ua";
- // const gql = (url, query, variables) =>
- // fetch(url, {
- // method: "POST",
- // headers: {
- // "Content-Type": "application/json",
- // Accept: "application/json",
- // },
- // body: JSON.stringify({ query, variables }),
- // }).then((res) => res.json());
- 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());
- 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("Поздравляем, вы оформили заказ!");
- }
- };
- const orderHistory = () =>
- actionPromise(
- "history",
- gql(` query OrderFind{
- OrderFind(query:"[{}]"){
- _id total createdAt orderGoods{
- count good{
- _id name price images{
- url
- }
- }
- owner{
- _id login
- }
- }
- }
- }
- `)
- );
- store.subscribe(() => {
- const { rootCats } = store.getState().promise;
- if (rootCats?.payload) {
- aside.innerHTML = "";
- for (let { _id, name } of rootCats?.payload) {
- const a = document.createElement("a");
- a.href = `#/category/${_id}`;
- a.innerHTML = name;
- aside.append(a);
- }
- }
- });
- store.subscribe(() => {
- const { catById } = store.getState().promise;
- const [, route] = location.hash.split("/");
- if (catById?.payload && route === "category") {
- const { name, goods, subCategories } = catById?.payload;
- categoryName.innerHTML = `<h1>${name}</h1>`;
- var element = document.getElementById("productBlock");
- while (element.firstChild) {
- element.removeChild(element.firstChild);
- }
- if (subCategories) {
- for (let { name, _id } of subCategories) {
- const link = document.createElement("a");
- link.id = "subCategories";
- link.href = `#/category/${_id}`;
- link.innerText = name;
- productBlock.append(link);
- }
- }
- for (let { _id, name, price, images } of goods) {
- const description = document.createElement("div");
- const textBlock = document.createElement("div");
- const imgProduct = document.createElement("img");
- const a = document.createElement("p");
- const productPrice = document.createElement("p");
- const linkCard = document.createElement("a");
- productBlock.append(linkCard);
- linkCard.href = `#/good/${_id}`;
- linkCard.append(description);
- description.setAttribute("class", "card");
- description.id = "card";
- description.append(imgProduct);
- imgProduct.src = `${backendURL}/${images[0].url}`;
- description.append(textBlock);
- a.innerHTML = name;
- textBlock.append(a);
- productPrice.innerHTML = "price: " + price;
- textBlock.append(productPrice);
- const addToCartButton = document.createElement("p");
- addToCartButton.innerText = "click to buy";
- addToCartButton.className = "addToCartButton";
- textBlock.append(addToCartButton);
- }
- }
- });
- const flexBlockForGFO = document.createElement("div");
- flexBlockForGFO.id = "flexBlockForGFO";
- const goodFineOneImgBlock = document.createElement("div");
- const goodFineOneTextBlock = document.createElement("div");
- const goodFineOneName = document.createElement("h2");
- const goodFineOneImg = document.createElement("img");
- const goodFineOnePrice = document.createElement("p");
- const goodFineOneDescription = document.createElement("p");
- const goodFineOneAddToCartButton = document.createElement("button");
- const buttonPlus = document.createElement("button");
- const buttonMinus = document.createElement("button");
- buttonPlus.innerHTML = "+";
- buttonMinus.innerHTML = "-";
- store.subscribe(() => {
- const { GoodFineOne } = store.getState().promise;
- const [, route, _id] = location.hash.split("/");
- if (GoodFineOne?.payload && route === "good") {
- productBlock.innerHTML = "";
- const { name, images, price, description } = GoodFineOne?.payload;
- productBlock.append(flexBlockForGFO);
- flexBlockForGFO.append(goodFineOneImgBlock);
- goodFineOneImg.src = `${backendURL}/${images[0].url}`;
- goodFineOneImg.id = "goodOneImg";
- goodFineOneImgBlock.append(goodFineOneImg);
- flexBlockForGFO.append(goodFineOneTextBlock);
- goodFineOneName.innerHTML = name;
- goodFineOneTextBlock.append(goodFineOneName);
- goodFineOnePrice.innerHTML = "price: " + price;
- goodFineOneTextBlock.append(goodFineOnePrice);
- goodFineOneDescription.innerHTML = description;
- goodFineOneTextBlock.append(goodFineOneDescription);
- goodFineOneAddToCartButton.innerHTML = "add to cart";
- goodFineOneTextBlock.append(goodFineOneAddToCartButton);
- goodFineOneAddToCartButton.onclick = () => {
- store.dispatch(actionCartAdd(GoodFineOne.payload));
- };
- }
- });
- const bPoputDeleteBlock = document.createElement("div");
- const bPoput = document.createElement("div");
- bPoput.className = "b-popup";
- bPoput.id = "b-popup";
- const bPoputContainer = document.createElement("div");
- bPoputContainer.className = "b-popup-content";
- bPoputContainer.id = "b-popup-content";
- const buttonGoodDeleteBlock = document.createElement("div");
- buttonGoodDeleteBlock.id = "buttonGoodDeleteBlock";
- const buttonCloseCart = document.createElement("button");
- buttonCloseCart.innerText = `×`;
- buttonCloseCart.id = "buttonCloseCartId";
- const buttonGoodDelete = document.createElement("button");
- buttonGoodDelete.innerText = "delete";
- buttonGoodDelete.id = "buttonDelete";
- shoppingCart.onclick = () => {
- header.append(bPoput);
- bPoput.append(bPoputContainer);
- };
- bPoputContainer.append(buttonGoodDeleteBlock);
- buttonGoodDeleteBlock.append(buttonGoodDelete);
- bPoputContainer.append(buttonCloseCart);
- const divToCardBlock = document.createElement("div");
- const goodByIdPrice = document.createElement("h2");
- const buyBlock = document.createElement("div");
- buyBlock.className = "buyBlock";
- const buttonBuy = document.createElement("button");
- buttonBuy.className = "buttonBuy";
- buttonBuy.id = "buttonBuy";
- store.subscribe(() => {
- divToCardBlock.innerHTML = "";
- goodByIdPrice.innerHTML = "";
- const toCartById = store.getState().cart;
- let countSum = 0;
- let priceSum = 0;
- for (let value of Object.values(toCartById)) {
- const { count, good, price } = value;
- countSum += count;
- priceSum += good.price * count;
- divToCardBlock.id = "divToCartBlock";
- const divToCart = document.createElement("div");
- const goodByIdImage = document.createElement("img");
- const goodByIdName = document.createElement("h2");
- const goodByIdCount = document.createElement("h2");
- const buttonPlus = document.createElement("button");
- const buttonMinus = document.createElement("button");
- buttonBuy.style.display = "block";
- buttonBuy.innerHTML = "Buy";
- goodByIdPrice.innerHTML = "Total: " + priceSum;
- buttonPlus.innerHTML = "+";
- buttonMinus.innerHTML = "-";
- buttonPlus.id = "buttonPlus";
- buttonMinus.id = "buttonMinus";
- divToCart.id = "divToCart";
- bPoputContainer.append(divToCardBlock);
- divToCardBlock.append(divToCart);
- divToCart.append(goodByIdImage);
- divToCart.append(goodByIdName);
- divToCart.append(goodByIdCount);
- divToCart.append(buttonPlus);
- divToCart.append(buttonMinus);
- bPoputContainer.append(buyBlock);
- buyBlock.append(goodByIdPrice);
- buyBlock.append(buttonBuy);
- goodByIdImage.src = `${backendURL}/${value.good.images[0].url}`;
- goodByIdName.innerText = good.name;
- goodByIdCount.innerText = count;
- buttonBuy.onclick = () => {
- store.dispatch(actionOrder());
- store.dispatch(orderHistory());
- };
- buttonPlus.onclick = () => store.dispatch(actionCartAdd(value.good));
- buttonMinus.onclick = () => {
- store.dispatch(actionCartDelete(value.good));
- console.log(value.good, "this");
- // if(value.good.count === 0)
- };
- }
- shoppingCart.innerHTML = "Cart: " + countSum;
- buttonCloseCart.onclick = () => {
- var parent = document.getElementById("header");
- var child = document.getElementById("b-popup");
- parent.removeChild(child);
- };
- const payload = store.getState().auth.token;
- if (payload) {
- shoppingCart.style.display = "block";
- } else {
- shoppingCart.style.display = "none";
- }
- });
- buttonGoodDelete.onclick = () => {
- store.dispatch(actionCartClear());
- let a = document.getElementById("divToCartBlock");
- a.innerHTML = "";
- let b = document.getElementById("shoppingCart");
- b.innerHTML = "Cart";
- let c = document.getElementById("buttonBuy");
- c.style.display = "none";
- };
- const goodByIdName = document.createElement("div");
- const h2text = document.createElement("h2");
- h2text.id = "h2text";
- qwer.append(h2text);
- const logoutButton = document.createElement("button");
- logoutButton.id = "logoutButton";
- qwer.append(logoutButton);
- store.subscribe(() => {
- const payload = store.getState().auth.token;
- if (payload) {
- logoutButton.style.display = "block";
- logoutButton.innerHTML = "Logout";
- login.style.display = "none";
- reg.style.display = "none";
- h2text.style.display = "block";
- h2text.innerText = jwtDecode(payload).sub.login;
- } else {
- h2text.style.display = "none";
- logoutButton.style.display = "none";
- }
- });
- // store.subscribe(() => {
- // const orders = store.dispatch()
- // })
- const buttonLogin = document.createElement("button");
- buttonLogin.id = "loginInputt";
- buttonLogin.innerText = "Login";
- const buttonReg = document.createElement("button");
- buttonReg.id = "regInput";
- buttonReg.innerText = "Registration";
- function bPopupCreate(text) {
- const bPopup = document.createElement("div");
- const bPopupContent = document.createElement("div");
- bPopup.id = "b-popup";
- bPopup.className = "b-popup";
- bPopupContent.className = "b-popup-content b-poput-container-flex";
- header.append(bPopup);
- bPopup.append(bPopupContent);
- const buttonCloseCart = document.createElement("button");
- buttonCloseCart.innerText = `×`;
- buttonCloseCart.id = "buttonCloseCartId";
- bPopupContent.append(buttonCloseCart);
- const loginText = document.createElement("h2");
- const passwordText = document.createElement("h2");
- loginText.innerText = "Enter Login:";
- bPopupContent.append(loginText);
- const loginInput = document.createElement("input");
- loginInput.type = "text";
- bPopupContent.append(loginInput);
- loginInput.id = "loginInput";
- loginInput.value = "illiaKozyr";
- passwordText.innerText = "Enter Password:";
- bPopupContent.append(passwordText);
- const loginInputPassword = document.createElement("input");
- loginInputPassword.type = "password";
- bPopupContent.append(loginInputPassword);
- loginInputPassword.id = "passwordInput";
- loginInputPassword.value = "qwerty123456";
- bPopupContent.append(text);
- buttonCloseCart.onclick = () => {
- var parent = document.getElementById("header");
- var child = document.getElementById("b-popup");
- parent.removeChild(child);
- };
- }
- window.onhashchange = () => {
- const [, route, _id] = location.hash.split("/");
- const routes = {
- category() {
- store.dispatch(actionCatById(_id));
- },
- good() {
- store.dispatch(actionGoodById(_id));
- },
- dashboard() {
- store.dispatch(orderHistory());
- },
- };
- if (route in routes) {
- routes[route]();
- }
- };
- window.onhashchange();
- store.dispatch(orderHistory());
- const h2 = document.createElement("h2");
- store.subscribe(() => {
- const { history } = store.getState().promise;
- const [, route] = location.hash.split("/");
- purchaseHistory.onclick = () => {
- const bPopup = document.createElement("div");
- const bPopupContent = document.createElement("div");
- bPopup.id = "b-popup";
- bPopup.className = "b-popup";
- bPopupContent.className = "b-popup-content b-poput-container-flex";
- header.append(bPopup);
- bPopup.append(bPopupContent);
- const buttonCloseCart = document.createElement("button");
- buttonCloseCart.innerText = `×`;
- buttonCloseCart.id = "buttonCloseCartId";
- bPopupContent.append(buttonCloseCart);
- for (let [key, value] of Object.entries(history.payload)) {
- const { _id, createdAt, total, orderGoods } = value;
- console.log(total, "this");
- const h2 = document.createElement("h2");
- h2.className = "h2History"
- const dateOfOrder = new Date(+createdAt);
- h2.innerHTML = `${dateOfOrder.toLocaleDateString()} ${dateOfOrder.toLocaleTimeString()}
- Order ID: ${_id} от , c ${orderGoods.length} goods worth: ${total}`;
- bPopupContent.append(h2);
-
-
- }
- if (Object.keys(history.payload).length == 0) {
- const p = document.createElement("p");
- p.innerHTML = "<p>No purchases made yet</p>";
- card.append(p);
- }
- buttonCloseCart.onclick = () => {
- var parent = document.getElementById("header");
- var child = document.getElementById("b-popup");
- parent.removeChild(child);
- };
- };
- });
- login.onclick = () => {
- bPopupCreate(buttonLogin);
- buttonLogin.onclick = () => {
- store.dispatch(actionFullLogin(loginInput.value, passwordInput.value));
- logoutButton.style.display = "block";
- var parent = document.getElementById("header");
- var child = document.getElementById("b-popup");
- parent.removeChild(child);
- purchaseHistory.style.display = "block";
- };
- };
- reg.onclick = () => {
- bPopupCreate(buttonReg);
- buttonReg.onclick = () => {
- store.dispatch(
- actionFullRegister(loginInput.value, passwordInput.value),
- store.dispatch(actionCartClear())
- );
- var parent = document.getElementById("header");
- var child = document.getElementById("b-popup");
- parent.removeChild(child);
- };
- };
- logoutButton.onclick = () => {
- store.dispatch(actionAuthLogout());
- login.style.display = "block";
- reg.style.display = "block";
- purchaseHistory.style.display = "none";
- };
- // purchaseHistory.onclick = () => {
- // }
|