/* const getGQL = url => (query,variables={}) => fetch(url,{ method:"POST", headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body:JSON.stringify({query,variables}) }).then((res) => res.json()) .then((data) => { if(data.errors && !data.data){ throw new Error(JSON.stringify(data.errors)) } else{ let value = Object.values(data.data); return value[0]; } }) */ //headers:{...{localStorage.authToken & {Autorization: `Bearer ${LocalStorage.authToken} : {}`}}} /* const getGQL = url =>{ return async (query,variables={}) =>{ let parametrs = {headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }} let authToken = localStorage.getItem("authToken") if(authToken){ parametrs.headers.Authorization = "Bearer" + authToken } parametrs.body = JSON.stringify({query,variables}) parametrs.method = "POST" let result = await fetch(url,parametrs) .then((res) => res.json()) if("data" in result) { return result.data[Object.keys(result.data)[0]] } else if("errors" in result){ throw new Error(JSON.stringify(result.errors)) } } } const backendURL = 'http://shop-roles.asmer.fs.a-level.com.ua' const gql = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql'); */ const getGQL = url => (query, variables) => fetch(url, { method: 'POST', headers: { "Content-Type": "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.asmer.fs.a-level.com.ua' const gql = getGQL(backendURL + '/graphql') /* (async()=>{ let data = await gql(`query login($login:String, $password:String){ login(login:$login, password:$password) }`, {login:'Anon', password:'Anon'}); localStorage.authToken = data; })(); */ /* gql(`query login($login:String, $password:String){ login(login:$login, password:$password) }`, {login:'Anon', password:'Anon'}) //login with gql function .then((data) => console.log(data)); */ /// some queries but works not good /* query CoutCat{ CategoryCount(query:"[{}]") } query findCategory { CategoryFind(query:"[{}]"){ name goods{name,categories{name}} parent{parent{name}} } } mutation newGood($newGood:GoodInput){ GoodUpsert(good:$newGood){ name price description categories{name} } } */ function promiseReducer(state={}, {type,name,status,payload,error}){ /* delay1000:{status, payload,error} delay2000: {status, payload,error}*/ if(!state){ delay1000:{status,payload,error} delay2000:{status,payload,error} } if(type === 'PROMISE'){ return{ ...state, [name]:{status,payload,error} } } return state } function createStore(promiseReducer){ let state = promiseReducer(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 = promiseReducer(state, action) //пробуем запустить редьюсер if (newState !== state){ //проверяем, смог ли редьюсер обработать action state = newState //если смог, то обновляем state for (let cb of cbs) cb() //и запускаем подписчиков } } return { getState, //добавление функции getState в результирующий объект dispatch, subscribe //добавление subscribe в объект } } const combineReducers = (reducers) => (state={},action) => { const newState = {} for(const [reduceName, reducer] of Object.entries(reducers)){ let newSubState = reducer(state[reduceName],action); if(state[reduceName] !== newSubState){ newState[reduceName] = newSubState } } if(Object.keys(newState).length){ state = {...state, ...newState} } return state; } /* let store = createStore(promiseReducer); store.subscribe(()=>console.log(store.getState())); */ const actionPending = name => ({name,type:"PROMISE",status:'PENDING'}) const actionFulfiled = (name,payload) =>({name,type:"PROMISE",status:'FULFILLED',payload}) const actionrRejected = (name,error) => ({name,type:"PROMISE",status:'REJECTED',error}) const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms)) /* store.subscribe(() => console.log(store.getState())); */ /* store.dispatch(actionPending('delay1000')) //{ 1 // delay1000: {status: 'PENDING'} //} delay(1000).then(result => store.dispatch(actionFulfiled('delay1000', result))) /* console.log(store.getState()); */ //{ 3 // delay1000: {status: 'FULFILLED', payload: 1000}, // delay2000: {status: 'PENDING'} //} /* store.dispatch(actionPending('delay2000')) //{ 2 // delay1000: {status: 'FULFILLED', payload: 1000}, // delay2000: {status: 'PENDING'} //} delay(2000).then(result => store.dispatch(actionFulfiled('delay2000', result))) */ //{ 4 // delay1000: {status: 'FULFILLED', payload: 1000}, // delay2000: {status: 'FULFILLED', payload: 2000} //} */ const jwtDecode = token =>{ try{ let payload = JSON.parse(atob(token.split('.')[1])); return payload; } catch(e){ } } console.log(jwtDecode(localStorage.getItem('authToken'))); 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.authToken = ''; return {}; } return state || {}; } function cartReducer(state={},{type, good, count=1}){ /* _id1: {count:1, good: {_id1,name,price,images}} _id2: {count:1, good: {_id2,name,price,images}} */ if (type === 'CART_ADD'){ /* if(good._id in state){ return{ ...state, [good._id]: {count:state[good._id].count+count,good} } }else{ */ return{ ...state, [good._id] : {count: count + (good._id in state ? state[good._id].count : 0),good} } } if (type === 'CART_CHANGE'){ /* let _id1 = good._id; console.log(_id1); */ return{ ...state, [good._id]: {count:count,good:good} } } if (type === 'CART_DELETE'){ /* const clone = {...state} delete clone[good._id] state = {...clone} */ let {[good._id]: remove, ...newState} = state; return newState; } if (type === 'CART_CLEAN'){ return {} } return state } const actionCartAdd = (good, count=1) => ({type:'CART_ADD', good, count}) const actionCartChange = (good, count=1) => ({type:'CART_CHANGE',good,count}) const actionCartDelete = (good) => ({type: 'CART_DELETE',good}) const actionCartClean = () => ({type:'CART_CLEAN'}) /* let store = createStore(authReducer); */ /* const store = createStore(promiseReducer); */ const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer,cart: cartReducer})) /* store.subscribe(() => console.log(store.getState())); store.dispatch(actionCartAdd({_id:'pivas',name:'cherniga',price:50})); store.dispatch(actionCartAdd({_id: 'пиво', name: 'одеколонь', price: 30})) store.dispatch(actionCartAdd({_id: 'пиво', name: 'одеколонь', price: 30},5)) store.dispatch(actionCartChange({_id: 'чипсы', name: 'одеколонь', price: 30}, 2)) store.dispatch(actionCartAdd({_id:'чипсы', name: 'одеколонь', price: 30})) store.dispatch(actionCartChange({_id: 'pivas', name: 'cherniga', price: 50}, 1)) store.dispatch(actionCartDelete({_id:'pivas',name:'cherniga'})); */ const actionPromise = (name,promise) => async dispatch => { dispatch(actionPending(name)) try{ let payload = await promise dispatch(actionFulfiled(name,payload)) return payload } catch(e){ dispatch(actionrRejected(name,e)) } } const actionAuthLogin = (tokennn) => ({type:'AUTH_LOGIN',token:tokennn}) 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 goods { _id name price images { url } } } }`, {q: JSON.stringify([{_id}])})) const actionGoodById = (_id) => actionPromise('goodById', gql(`query goodById ($goodId: String){ GoodFindOne(query:$goodId){ _id name price images{ url } description } }`,{goodId: JSON.stringify([{_id}])})) const actionFullRegister = (Login,password) => actionPromise('fullRegister', gql(`mutation register($Login:String,$password:String){ UserUpsert(user:{login:$Login,password:$password}){ _id login } }`,{Login: Login, password: password})) const actionFullLogin = (Login,password) => async (dispatch) => { const tokennn = await dispatch( actionPromise('fullLogin', gql(`query login($Login:String,$password:String){ login(login:$Login,password:$password) }`,{Login:Login,password:password})) ); await dispatch(actionAuthLogin(tokennn)); } const actionAllOrders = () => actionPromise('allOrders',gql(`query orders{ OrderFind(query:"[{}]"){ _id total orderGoods{ count total good{name,price, images{ url }} } } }`)) const actionCreateOrder = () => async (dispatch) =>{ let orderGoods = []; Object.entries(store.getState().cart).map(([_id,{count}])=>orderGoods.push({"count":count,"good":{_id:_id}})) actionPromise('createOrder',gql(`mutation newOrder($order:OrderInput){ OrderUpsert(order:$order){ _id total } }`,{order:{orderGoods}})); await store.dispatch(actionCartClean()); } const actionSearchGood = (good) => actionPromise('searchGood', gql(`query gf($query: String){ GoodFind(query: $query){ _id, name, description, price, images{ _id, url } } }`, {query: JSON.stringify([ { $or: [{name: `/${good}/`}, {description: `/${good}/`}] //регулярки пишутся в строках }, { sort: [{name: 1}]} //сортируем по title алфавитно ]) })) store.dispatch(actionRootCats()) store.subscribe(() => { const {promise:{rootCats}} = store.getState() if (rootCats?.payload){ aside.innerHTML = '' for (const {_id, name} of rootCats?.payload){ const link = document.createElement('a') link.href = `#/category/${_id}` link.innerText = name aside.append(link) } } }) window.onhashchange = () => { const [, route, _id] = location.hash.split('/') const routes = { category(){ store.dispatch(actionCatById(_id)) }, good(){ //задиспатчить actionGoodById store.dispatch(actionGoodById(_id)); }, login(){ //отрисовка тут /* store.subscribe(() => { */ const [,route,id] = location.hash.split('/') if(route === 'login'){ main.innerHTML = `
${cart[good].good.name}
` */ let goods = document.createElement('div'); let img = document.createElement('img'); img.src = `${backendURL}/${cart[good].good.images[0].url}`; let name = document.createElement('p'); name.innerText = `${cart[good].good.name}` let count = document.createElement('input'); count.type = 'number'; count.min = 1; count.value = cart[good].count; let countPlus = document.createElement('button'); countPlus.innerText = '+' let countMinus = document.createElement('button'); countMinus.innerText = '-'; countPlus.onclick = () => { cart[good].count++; count.value = cart[good].count; store.dispatch(actionCartChange(cart[good].good, +count.value)) } countMinus.onclick = () =>{ cart[good].count--; count.value = cart[good].count; if(cart[good].count < 1){ count.value = 1; cart[good].count = 1; store.dispatch(actionCartChange(cart[good].good, +count.value)) } store.dispatch(actionCartChange(cart[good].good, +count.value)) } count.oninput = () => { store.dispatch(actionCartChange(cart[good].good, +count.value)) } let removeBtn = document.createElement('button'); removeBtn.innerText = 'Remove'; removeBtn.onclick = () => { store.dispatch(actionCartDelete(cart[good].good)) window.onhashchange() cartIcon.innerHTML = `Cart ${cart[good].count}` } goods.append(img); goods.append(name); goods.append(count); goods.append(countPlus); goods.append(countMinus); goods.append(removeBtn); main.append(goods); } let orderButton = document.createElement('button'); orderButton.innerText = 'Order'; orderButton.onclick = () => { store.dispatch(actionCreateOrder()); window.onhashchange() cartIcon.innerHTML = `Cart` } if(localStorage.authToken && Object.keys(cart).length !== 0){ orderButton.disabled = "" } else { orderButton.disabled = "true" } main.append(orderButton); } } } if (route in routes) routes[route]() } window.onhashchange() store.subscribe(() => { const {promise: {catById}} = store.getState(); const [,route,_id] = location.hash.split('/'); if(catById?.payload && route === 'category'){ const {name} = catById.payload; main.innerHTML = `${description}
${price}UAH ` main.append(card); let buyGood = document.getElementById('buyGood'); buyGood.addEventListener('click', function addToCart(){ console.log(id); store.dispatch(actionCartAdd(store.getState().promise.goodById.payload)); }) } }) store.subscribe(() => { let counts = 0; const cartIcon = document.getElementById('cartIcon'); const orders = store.getState().cart; for (let order in orders){ counts += orders[order].count cartIcon.innerHTML = `Cart ${counts}` /* if(!orders[order].count){ cartIcon.innerHTML = `Cart ${counts}` } */ } }) store.subscribe(async() => { const[,route,id] = location.hash.split('/') const {promise: {allOrders}} = await store.getState(); if(route === 'dashboard' && allOrders.payload){ main.innerHTML = `${count}
${total}
` order.append(card); } main.append(order); } } }) let inputSearch = document.createElement('input'); inputSearch.type = "text"; let header = document.getElementById('header'); header.append(inputSearch); let divWithGood = document.createElement('div'); divWithGood.style.display = 'none'; header.append(divWithGood); function render(parent,data){ console.log(data); } store.subscribe(() => { let re = /\s* \s*/; inputSearch.oninput = () => { let newStr = inputSearch.value.trim().split(re).join("|"); store.dispatch(actionSearchGood(newStr)); } inputSearch.onfocus = () => { divWithGood.style.display = 'block'; const searchGood = store.getState().promise.searchGood; render(divWithGood,searchGood); } inputSearch.blur = () => { divWithGood.style.display = 'none'; render(divWithGood,searchGood); } }) let token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2MWJiNjNjOWM3NTBjMTJiYTZiYTRiMzgiLCJsb2dpbiI6IkFub24iLCJhY2wiOlsiNjFiYjYzYzljNzUwYzEyYmE2YmE0YjM4IiwidXNlciJdfSwiaWF0IjoxNjQwMjAwNTU2fQ.P_JXFko7OFObxVn8uB6TR3R1V9aB1MMQrgX58MpEq5E'; /* store.dispatch(actionPromise('delay1000', delay(1000))) store.dispatch(actionPromise('delay2000', delay(2000))) */ // store.dispatch(actionAuthLogin(token)) const actionAuthLogout= () => ({type: 'AUTH_LOGOUT'}) // store.dispatch(actionAuthLogout()); /* console.log(store.getState()); */ /* const actionRegister = (login,password) => actionPromise('register', gql(`mutation register($Login:String,$password:String){ UserUpsert(user:{login:$Login,password:$password}){ _id login } }`,{login:login,password:password})); store.dispatch(actionRegister('aufff', '123456')); */ /* const combinedStore = createStore(combineReducers({promise: promiseReducer, auth: authReducer})) */ //search // let re = /\s* \s*/; // let input = document.createElement('input'); // input.type = 'text'; // document.body.append(input); // input.oninput = () => { // let newStr = input.value.trim().split(re).join("|"); // console.log(`/${newStr}/`); // }