123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716 |
- function createStore(reducer) {
- let state = reducer(undefined, {})
- let cbs = []
- function dispatch(action) {
- if (typeof action === 'function') {
- return action(dispatch)
- }
- const newState = reducer(state, action)
- if (state !== newState) {
- state = newState
- cbs.forEach(cb => cb())
- }
- }
- return {
- dispatch,
- subscribe(cb) {
- cbs.push(cb)
- return () => cbs = cbs.filter(c => c !== cb)
- },
- getState() {
- return state
- }
- }
- }
- //reducers
- function promiseReducer(state = {}, { type, status, payload, error, name }) {
- if (type === 'PROMISE') {
- return {
- ...state,
- [name]: { status, payload, error }
- }
- }
- return state
- }
- function cartReducer(state = {}, { type, count = 1, _id, name }) {
- if (type === "CART_ADD") {
- return {
- ...state,
- [_id]: {
- name: name,
- count: state[_id] ? state[_id].count + count : count
- }
- }
- }
- if (type === "CART_CHANGE") {
- return {
- ...state,
- [_id]: {
- name: name,
- count: count
- }
- }
- }
- if (type === "CART_REMOVE") {
- let { [_id]: a, ...rest } = state
- return rest
- }
- if (type === "CART_CLEAR") {
- return {}
- }
- return state
- }
- function authReducer(state, { type, token }) {
- if (state === undefined) {
- if (localStorage.authToken) {
- type = "LOGIN"
- token = localStorage.authToken
- } else {
- return {}
- }
- }
- if (type === "LOGIN") {
- localStorage.authToken = token
- return { token, payload: jwt_decode(token) }
- }
- if (type === "LOGOUT") {
- localStorage.removeItem("authToken")
- return {}
- }
- return state
- }
- let reducers = {
- promise: promiseReducer,
- cart: cartReducer,
- auth: authReducer
- }
- let jwt_decode = (token) => {
- let result = JSON.parse(atob(token.split(".")[1]))
- return result
- }
- function combineReducers(reducers) {
- function commonReducer(state = {}, action) {
- let newState = {}
- for (let key in reducers) {
- let innerState = reducers[key](state[key], action)
- innerState === state[key] ? newState[key] = state[key] : newState[key] = innerState
- }
- return newState
- }
- return commonReducer
- }
- const store = createStore(combineReducers(reducers))
- //запросики
- const getGQL = url => {
- return (query, variables) => {
- return fetch(url, {
- method: 'POST',
- headers: {
- "content-type": "application/json",
- ...(localStorage.authToken ? { Authorization: "Bearer " + localStorage.authToken } : {})
- },
- body: JSON.stringify({ query, variables }),
- }).then(res => res.json())
- }
- }
- let shopGQL = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql')
- let categoryById = async (id) => {
- let query = `query fndcategory($id: String) {
- CategoryFind(query: $id){
- name goods{
- _id name price images {
- url
- }
- }
- }
- }`
- let qVariables = {
- "id": JSON.stringify([{ "_id": id }])
- }
- let res = await shopGQL(query, qVariables)
- console.log(res)
- return res
- }
- let goodById = async (id) => {
- let query = `query fndgood($id: String) {
- GoodFind(query: $id){
- name description price images {
- url
- }
- }
- }`
- let qVariables = {
- "id": JSON.stringify([{ "_id": id }])
- }
- let res = await shopGQL(query, qVariables)
- return res
- }
- let newOrder = async (obj) => {
- let option = Object.entries(obj);
- let orderGoods = [];
- for (let key of option) {
- let i = {
- "count": key[1],
- "good": { "_id": key[0] }
- }
- orderGoods.push(i);
- }
- let query = `mutation sndOrder($order: OrderInput) {
- OrderUpsert(order: $order) {
- _id createdAt total
- }
- }`
- let qVariables = {
- "order": {
- "orderGoods": orderGoods
- }
- }
- let res = await shopGQL(query, qVariables)
- console.log(res)
- return res
- }
- let log = async (login, password) => {
- let query = `query log($l: String, $p: String) {
- login(login: $l, password: $p)
- }`
- let qVariables = {
- "l": login,
- "p": password
- }
- let token = await shopGQL(query, qVariables)
- console.log(token)
- return token.data.login
- }
- let reg = async (login, password) => {
- let query = `mutation reg($l: String, $p: String) {
- UserUpsert(user: {
- login: $l,
- password: $p
- } ) {
- _id
- }
- }`
- let qVariables = {
- "l": login,
- "p": password
- }
- let res = await shopGQL(query, qVariables)
- return res
- }
- //actions
- const actionPending = name => ({ type: 'PROMISE', status: 'PENDING', name })
- const actionResolved = (name, payload) => ({ type: 'PROMISE', status: 'RESOLVED', name, payload })
- const actionRejected = (name, error) => ({ type: 'PROMISE', status: 'REJECTED', name, error })
- const actionPromise = (name, promise) =>
- async dispatch => {
- dispatch(actionPending(name))
- try {
- let payload = await promise
- dispatch(actionResolved(name, payload))
- return payload
- }
- catch (error) {
- dispatch(actionRejected(name, error))
- }
- }
- const actionRootCategories = () =>
- actionPromise('rootCategories', shopGQL(`
- query cats($query:String){
- CategoryFind(query:$query){
- _id name
- }
- }
- `, { query: JSON.stringify([{ parent: null }]) }))
- const actionCategoryById = id => actionPromise('catById', categoryById(id))
- const actionGoodById = id => actionPromise('goodById', goodById(id))
- const actionBuyGood = (obj) => actionPromise('newOrder', newOrder(obj))
- const actionCartAdd = (n, id, name) => ({ type: "CART_ADD", count: n, _id: id, name })
- const actionCartChange = (n, id, name) => ({ type: "CART_CHANGE", count: n, _id: id, name })
- const actionCartRemove = id => ({ type: "CART_REMOVE", _id: id })
- const actionCartClear = () => ({ type: "CART_CLEAR" })
- const actionAuthLogin = token => ({ type: "LOGIN", token })
- const actionAuthLogout = () => ({ type: "LOGOUT" })
- const actionLogin = (login, password) => actionPromise("login", log(login, password))
- const actionFullLogin = (login, password) => {
- return async (dispatch) => {
- let result = await dispatch(actionLogin(login, password))
- dispatch(actionAuthLogin(result))
- }
- }
- const actionRegister = (login, password) => actionPromise("register", reg(login, password))
- const actionFullRegister = (login, password) => {
- return async (dispatch) => {
- let result = await dispatch(actionRegister(login, password))
- if (result.data.UserUpsert !== null) {
- dispatch(actionFullLogin(login, password))
- }
- }
- }
- store.dispatch(actionRootCategories())
- window.onhashchange = () => {
- let { 1: route, 2: id } = location.hash.split('/')
- if (route === 'categories') {
- store.dispatch(actionCategoryById(id))
- }
- if (route === 'good') {
- store.dispatch(actionGoodById(id))
- }
- if (route === 'cart') {
- cartDraw(store.getState().cart, main)
- }
- if (route === "login") {
- loginDraw(main)
- }
- if (route === "registration") {
- registrationDraw(main)
- }
- }
- //рисовашки
- function drawMainMenu() {
- let cats = store.getState().promise.rootCategories.payload
- if (cats) { //каждый раз дорисовываются в body
- aside.innerText = ''
- for (let { _id, name } of cats.data.CategoryFind) {
- let catA = document.createElement('a')
- catA.href = `#/categories/${_id}`
- catA.innerText = name
- aside.append(catA)
- }
- }
- }
- let goodDraw = (obj, parent, _id) => {
- let box = document.createElement("div")
- let goodName = document.createElement("h2")
- let goodIMG = document.createElement("img")
- let description = document.createElement("p")
- let price = document.createElement("span")
- let count = document.createElement("input")
- let goodBtnBox = document.createElement("div")
- let btn1 = document.createElement("button")
- let btn2 = document.createElement("button")
- price.textContent = "Цена: " + obj.price + "грн"
- goodIMG.src = "http://shop-roles.asmer.fs.a-level.com.ua/" + obj.images[0].url
- box.style.width = "40%"
- box.style.margin = "20px"
- box.style.display = "flex"
- box.style.flexDirection = "column"
- goodName.textContent = obj.name
- description.textContent = obj.description
- count.type = "number"
- count.min = 1
- count.value = 1
- goodBtnBox.style.display = "flex"
- goodBtnBox.style.alignItems = "center"
- btn1.textContent = "Купить"
- btn2.textContent = "В корзину"
- goodBtnBox.append(count, btn2, btn1)
- let order = {
- [_id]: +count.value
- }
- count.oninput = () => order[_id] = +count.value
- btn1.onclick = () => {
- store.dispatch(actionBuyGood(order))
- }
- btn2.onclick = () => {
- store.dispatch(actionCartAdd(+count.value, _id, obj.name))
- }
- box.append(goodIMG, goodName, description, price, goodBtnBox)
- parent.append(box)
- }
- let cardDraw = (obj, parent) => {
- let box = document.createElement("div");
- let img = document.createElement("img");
- let price = document.createElement("span");
- let productName = document.createElement("h5");
- img.src = "http://shop-roles.asmer.fs.a-level.com.ua/" + obj.images[0].url;
- productName.textContent = obj.name
- price.textContent = "Цена: " + obj.price + " грн"
- let productBody = document.createElement("div")
- box.className = "card"
- productBody.append(productName, price)
- box.append(img, productBody)
- let a = document.createElement("a")
- a.href = "#/good/" + obj._id
- a.append(box)
- parent.append(a)
- }
- let cartDraw = (obj, parent) => {
- while (parent.lastChild) {
- parent.lastChild.remove()
- }
- let cartName = document.createElement("h2")
- cartName.textContent = "Ваш заказ"
- parent.append(cartName)
- let order = {}
- for (let key in obj) {
- order[key] = obj[key].count
- let cartItem = document.createElement("div")
- let name = document.createElement("span")
- let count = document.createElement("input")
- let btnDelItem = document.createElement("button")
- let btnPlus = document.createElement("button")
- let btnMinus = document.createElement("button")
- count.min = 1
- count.value = obj[key].count
- count.className = "cart-item-count"
- btnPlus.className = "btn-plus"
- btnMinus.className = "btn-minus"
- btnDelItem.className = "btn-del"
- cartItem.className = "cart-item"
- name.textContent = obj[key].name
- cartItem.append(btnMinus, count, btnPlus, name, btnDelItem)
- parent.append(cartItem)
- if (+count.value === 1) {
- btnMinus.disabled = "true"
- }
- count.oninput = () => {
- if (+count.value >= 1) {
- store.dispatch(actionCartChange(+count.value, key, obj[key].name))
- }
- }
- btnPlus.onclick = () => {
- store.dispatch(actionCartChange(+count.value + 1, key, obj[key].name))
- }
- btnMinus.onclick = () => {
- store.dispatch(actionCartChange(+count.value - 1, key, obj[key].name))
- }
- btnDelItem.onclick = () => {
- store.dispatch(actionCartRemove(key))
- }
- }
- let btnBox = document.createElement("div")
- let btnOrder = document.createElement("button")
- let btnClear = document.createElement("button")
- btnClear.textContent = "ОЧИСТИТЬ КОРЗИНУ"
- btnClear.className = "btn-clear"
- btnOrder.textContent = "КУПИТЬ"
- btnOrder.onclick = () => {
- store.dispatch(actionBuyGood(order))
- store.dispatch(actionCartClear())
- }
- btnClear.onclick = () => {
- store.dispatch(actionCartClear())
- }
- btnBox.append(btnOrder, btnClear)
- parent.append(btnBox)
- }
- let cartValueDraw = () => {
- if (cart.children.length === 1) {
- let span = document.createElement("span")
- cart.append(span)
- }
- let sum = 0;
- let cartState = store.getState().cart
- for (let key in cartState) {
- sum += cartState[key].count
- }
- cart.lastChild.textContent = sum
- }
- let userStatus = (parent = userpanel) => {
- if (parent.children.length > 1) {
- parent.lastChild.remove()
- }
- let status = store.getState().auth
- let box = document.createElement("div")
- box.style.display = "flex"
- box.style.alignItems = "center"
- let userName = document.createElement("span")
- box.append(userName)
- parent.append(box)
- if (status.payload) {
- let btn = document.createElement("button")
- userName.textContent = status.payload.sub.login
- btn.textContent = "Выход"
- btn.onclick = () => {
- store.dispatch(actionAuthLogout())
- }
- box.append(btn)
- } else {
- let btn = document.createElement("a")
- let btnREG = document.createElement("a")
- userName.textContent = "Гость"
- btn.textContent = "Вход"
- btnREG.textContent = "Регистрация"
- btn.href = "#/login"
- btnREG.href = "#/registration"
- box.append(btn, btnREG)
- }
- }
- function Form(parent, type, open) {
- let h3 = document.createElement("h3");
- let loginInput = document.createElement("input");
- let passwordInput = document.createElement("input");
- let checkbox = document.createElement("input");
- let btn = document.createElement("button");
- let form = document.createElement("div");
- let box = document.createElement("div")
- box.style.display = "flex"
- box.style.alignItems = "center"
- box.append(passwordInput, checkbox)
- loginInput.id = "login"
- loginInput.placeholder = "логин"
- passwordInput.id = "password"
- passwordInput.placeholder = "пароль"
- btn.id = "btn"
- checkbox.type = "checkbox";
- checkbox.style.marginLeft = "5px"
- btn.disabled = true;
- if (type === "reg") {
- h3.textContent = "РЕГИСТРАЦИЯ"
- btn.textContent = "ЗАРЕГИСТРИРОВАТЬСЯ";
- } else {
- h3.textContent = "ВХОД"
- btn.textContent = "Войти";
- }
- form.append(h3, loginInput, box, btn)
- form.className = "form"
- parent.append(form)
- let btnOpen = () => {
- if (type === "reg" && checkbox.checked === false) {
- passwordInput.value !== "" && password.value === passwordVerify.value ? btn.disabled = false : btn.disabled = true;
- } else {
- (loginInput.value != "" && passwordInput.value != "") ? btn.disabled = false : btn.disabled = true;
- }
- }
- let checker = (check) => {
- if (check) {
- passwordInput.type = "text";
- checkbox.checked = true;
- if (type === "reg" && passwordVerify) {
- passwordVerify.remove()
- }
- } else {
- passwordInput.type = "password";
- checkbox.checked = false;
- if (type === "reg") {
- let passwordInput2 = document.createElement("input");
- passwordInput2.placeholder = "повторите пароль"
- passwordInput2.id = "passwordVerify"
- passwordInput2.type = "password"
- passwordInput2.oninput = () => {
- btnOpen();
- }
- form.append(passwordInput2)
- }
- }
- }
- checker(open);
- loginInput.oninput = () => {
- btnOpen();
- if (typeof this.onChange === "function") {
- this.onChange([loginInput.value, passwordInput.value]);
- }
- }
- passwordInput.oninput = () => {
- btnOpen();
- if (typeof this.onChange === "function") {
- this.onChange([loginInput.value, passwordInput.value]);
- }
- }
- checkbox.onchange = () => {
- checker(checkbox.checked)
- btnOpen()
- if (typeof this.onOpenChange === "function") {
- this.onOpenChange(checkbox.checked);
- }
- }
- this.getValue = () => [loginInput.value, passwordInput.value];
- this.setValue = (valueLogin, valuePassword) => {
- loginInput.value = valueLogin;
- passwordInput.value = valuePassword;
- btnOpen();
- }
- this.getOpen = () => checkbox.checked;
- this.setOpen = (open) => {
- checker(open);
- }
- }
- let loginDraw = (parent) => {
- while (parent.lastChild) {
- parent.lastChild.remove()
- }
- if (store.getState().auth.payload) {
- let h2 = document.createElement("h2")
- h2.textContent = "ВЫ ВОШЛИ"
- h2.style.color = "grey"
- parent.append(h2)
- } else {
- let form = new Form(parent)
- btn.onclick = () => {
- store.dispatch(actionFullLogin(login.value, password.value))
- }
- }
- }
- let registrationDraw = (parent) => {
- while (parent.lastChild) {
- parent.lastChild.remove()
- }
- if (store.getState().auth.payload) {
- let h2 = document.createElement("h2")
- h2.textContent = "ВЫ УСПЕШНО ЗАРЕГИСТРИРОВАЛИСЬ"
- h2.style.color = "grey"
- parent.append(h2)
- } else if (store.getState().promise.register && store.getState().promise.register.status === "RESOLVED") {
- let h3 = document.createElement("h3")
- h3.textContent = "Error: " + store.getState().promise.register.payload.errors[0].message
- h3.style.color = "red"
- parent.append(h3)
- } else {
- let form = new Form(parent, "reg")
- btn.onclick = () => {
- store.dispatch(actionFullRegister(login.value, password.value))
- }
- }
- }
- //subscribers
- const unsubscribe1 = store.subscribe(() => console.log(store.getState()))
- store.subscribe(drawMainMenu)
- store.subscribe(cartValueDraw)
- store.subscribe(userStatus)
- store.subscribe(() => {
- const { 1: route, 2: id } = location.hash.split('/')
- if (route === 'categories') {
- const catById = store.getState().promise.catById?.payload
- if (catById) {
- while (main.lastChild) {
- main.lastChild.remove()
- }
- let categoryName = document.createElement("h2")
- let cards = document.createElement("div")
- cards.className = "cards"
- categoryName.textContent = catById.data.CategoryFind[0].name
- for (let key of catById.data.CategoryFind[0].goods) {
- cardDraw(key, cards)
- }
- main.append(categoryName, cards)
- }
- }
- if (route === 'good') {
- const goodById = store.getState().promise.goodById?.payload
- if (goodById) { //вывести в main страницу товара
- while (main.lastChild) {
- main.lastChild.remove()
- }
- goodDraw(goodById.data.GoodFind[0], main, id)
- }
- }
- if (route === 'cart') {
- cartDraw(store.getState().cart, main)
- }
- if (route === 'login') {
- loginDraw(main)
- }
- if (route === "registration") {
- registrationDraw(main)
- }
- })
|