|
@@ -0,0 +1,741 @@
|
|
|
+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 promiseReducer(state={}, {type, name, status, payload, error}){
|
|
|
+ ////?????
|
|
|
+ //ОДИН ПРОМИС:
|
|
|
+ //состояние: PENDING/FULFILLED/REJECTED
|
|
|
+ //результат
|
|
|
+ //ошибка:
|
|
|
+ //{status, payload, error}
|
|
|
+ //{
|
|
|
+ // name1:{status, payload, error}
|
|
|
+ // name2:{status, payload, error}
|
|
|
+ // name3:{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))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+const goToMainPage = () => location.href = location.href.split("#")[0];
|
|
|
+
|
|
|
+const checkAuthToken = () => {
|
|
|
+ const headers = {
|
|
|
+ "Content-Type": "application/json",
|
|
|
+ "Accept": "application/json",
|
|
|
+ }
|
|
|
+ if(localStorage.getItem('authToken')) {
|
|
|
+ return {
|
|
|
+ ...headers,
|
|
|
+ "Authorization": `Bearer ${localStorage.getItem('authToken')}`
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return headers;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getGQL = url =>
|
|
|
+ (query, variables= {}) =>
|
|
|
+ fetch(url, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: checkAuthToken(),
|
|
|
+ body:JSON.stringify({query, variables})
|
|
|
+ }).then(res => res.json())
|
|
|
+ .then(data => {
|
|
|
+ try {
|
|
|
+ if(!data.data && data.errors) {
|
|
|
+ throw new SyntaxError(`SyntaxError - ${JSON.stringify(Object.values(data.errors)[0])}`);
|
|
|
+ }
|
|
|
+ return Object.values(data.data)[0];
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+function jwtDecode(token){
|
|
|
+ try {
|
|
|
+ return JSON.parse(atob(token.split('.')[1]))
|
|
|
+ }
|
|
|
+ catch(e){
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function authReducer(state={}, {type, token}){
|
|
|
+ //{
|
|
|
+ // token,payload (раскодированный токен)
|
|
|
+ //} или, если не залогинены
|
|
|
+ //{
|
|
|
+ // нихрена, т. .е. пустой объект
|
|
|
+ //}
|
|
|
+ if (type === 'AUTH_LOGIN'){ //то мы логинимся
|
|
|
+ const payload = jwtDecode(token)
|
|
|
+ if (payload){
|
|
|
+ return {
|
|
|
+ token,
|
|
|
+ payload
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (type === 'AUTH_LOGOUT'){ //мы разлогиниваемся
|
|
|
+ return {}
|
|
|
+ }
|
|
|
+ return state
|
|
|
+}
|
|
|
+
|
|
|
+const actionAuthLogout = () =>
|
|
|
+ dispatch => {
|
|
|
+ dispatch({type: 'AUTH_LOGOUT'});
|
|
|
+ localStorage.removeItem('authToken');
|
|
|
+ goToMainPage()
|
|
|
+ }
|
|
|
+
|
|
|
+const actionAuthLogin = (token) =>
|
|
|
+ (dispatch, getState) => {
|
|
|
+ const oldState = getState()
|
|
|
+ dispatch({type: 'AUTH_LOGIN', token})
|
|
|
+ const newState = getState()
|
|
|
+ if (oldState !== newState)
|
|
|
+ localStorage.setItem('authToken', token)
|
|
|
+ }
|
|
|
+
|
|
|
+function cartReducer(state={}, {type, amount=1, good}){
|
|
|
+ /*
|
|
|
+ {
|
|
|
+ id1: {amount, good: {объект с бэка с _id, description, name, price}},
|
|
|
+ id2: {amount, good: {объект с бэка с _id, description, name, price}},
|
|
|
+ id3: {amount, good: {объект с бэка с _id, description, name, price}}
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+ if (type === 'CART_ADD'){
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ [good._id]: {good, amount: (state[good._id]?.amount || 0) + amount }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (type === 'CART_SET'){
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ [good._id]: {good, amount}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (type === 'CART_DELETE'){
|
|
|
+ const {[good._id]: skip,...newState} = state
|
|
|
+ //const newState = { ...state }
|
|
|
+ //delete newState[good._id]
|
|
|
+ return newState
|
|
|
+ }
|
|
|
+ if (type === 'CART_CLEAR'){
|
|
|
+ return {}
|
|
|
+ }
|
|
|
+ //if (type === ''){
|
|
|
+
|
|
|
+ //}
|
|
|
+
|
|
|
+ return state
|
|
|
+}
|
|
|
+
|
|
|
+const actionCartAdd = (good, amount=1) => ({type: 'CART_ADD', good, amount})
|
|
|
+const actionCartSet = (good, amount=1) => ({type: 'CART_SET', good, amount})
|
|
|
+const actionCartClear = () => ({type: 'CART_CLEAR'})
|
|
|
+const actionCartDelete = (good) => ({type: 'CART_DELETE', good})
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function combineReducers(reducers){
|
|
|
+ function totalReducer(state={}, action){
|
|
|
+ //{
|
|
|
+ //promise:{
|
|
|
+ //name1:{status, payload, error},
|
|
|
+ //name2:{status, payload, error}
|
|
|
+ //},
|
|
|
+ //auth: {
|
|
|
+ //token, payload
|
|
|
+ //}
|
|
|
+ //}
|
|
|
+ const newTotalState = {}
|
|
|
+ for (const [reducerName, reducer] of Object.entries(reducers)){
|
|
|
+ const newSubState = reducer(state[reducerName], action)
|
|
|
+ if (newSubState !== state[reducerName]){
|
|
|
+ newTotalState[reducerName] = newSubState
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (Object.keys(newTotalState).length){
|
|
|
+ return {...state, ...newTotalState}
|
|
|
+ }
|
|
|
+
|
|
|
+ return state
|
|
|
+ }
|
|
|
+
|
|
|
+ return totalReducer
|
|
|
+}
|
|
|
+
|
|
|
+function localStoredReducer(reducer, localStorageKey){
|
|
|
+ function wrapperReducer(state, action){
|
|
|
+ if (state === undefined){ //если загрузка сайта
|
|
|
+ try {
|
|
|
+ return JSON.parse(localStorage[localStorageKey]) //пытаемся распарсить сохраненный
|
|
|
+ //в localStorage state и подсунуть его вместо результата редьюсера
|
|
|
+ }
|
|
|
+ catch(e){ } //если распарсить не выйдет, то код пойдет как обычно:
|
|
|
+ }
|
|
|
+ const newState = reducer(state, action)
|
|
|
+ localStorage.setItem(localStorageKey, JSON.stringify(newState)) //сохраняем состояние в localStorage
|
|
|
+ return newState
|
|
|
+ }
|
|
|
+ return wrapperReducer
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+const reducers = {
|
|
|
+ auth: authReducer,
|
|
|
+ cart: localStoredReducer(cartReducer, 'cart'), //в localStorage должен появится ключ cart с JSON стейта корзины
|
|
|
+ promise: localStoredReducer(promiseReducer, 'promise'),
|
|
|
+}
|
|
|
+
|
|
|
+const totalReducer = combineReducers(reducers)
|
|
|
+
|
|
|
+
|
|
|
+const store = createStore(totalReducer) //не забудьте combineReducers если он у вас уже есть
|
|
|
+store.subscribe(() => console.log(store.getState()))
|
|
|
+
|
|
|
+
|
|
|
+const backendURL = 'http://shop-roles.node.ed.asmer.org.ua/'
|
|
|
+
|
|
|
+const gql = getGQL(backendURL + 'graphql')
|
|
|
+
|
|
|
+const rootCategories = () =>
|
|
|
+ gql(`query cadz($q:String) {
|
|
|
+ CategoryFind(query:$q){
|
|
|
+ _id name
|
|
|
+ }
|
|
|
+ }`, {q: JSON.stringify([{parent: null}])})
|
|
|
+
|
|
|
+const actionRootCategories = () =>
|
|
|
+ actionPromise('rootCategories', rootCategories())
|
|
|
+
|
|
|
+const categoryById = _id => //добавьте сюда подкатегории и родителя - пригодятся
|
|
|
+ gql(`query catById($qCat:String){
|
|
|
+ CategoryFindOne(query:$qCat){
|
|
|
+ _id name
|
|
|
+ parent {
|
|
|
+ _id name
|
|
|
+ }
|
|
|
+ subCategories {
|
|
|
+ name _id parent {
|
|
|
+ _id name
|
|
|
+ }
|
|
|
+ }
|
|
|
+ goods{
|
|
|
+ _id name price images{
|
|
|
+ url
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }`, {qCat: JSON.stringify([{_id}])})
|
|
|
+
|
|
|
+const actionCategoryById = _id =>
|
|
|
+ actionPromise('catById', categoryById(_id))
|
|
|
+
|
|
|
+const goodById = _id =>
|
|
|
+ gql(`query goodById($goodId:String) {
|
|
|
+ GoodFindOne(query:$goodId) {
|
|
|
+ _id name price description images {
|
|
|
+ url
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }`, {goodId: JSON.stringify([{_id}])})
|
|
|
+
|
|
|
+const actionGoodById = _id =>
|
|
|
+ actionPromise('goodById', goodById(_id));
|
|
|
+
|
|
|
+const actionOrder = () =>
|
|
|
+ async (dispatch, getState) => {
|
|
|
+ const order = Object.values(getState().cart).map(orderGoods => ({good: {_id: orderGoods.good._id}, count: orderGoods.amount}));
|
|
|
+ const myOrder = await dispatch(actionPromise('myOrder', gql(`mutation myOrder($order:OrderInput) {
|
|
|
+ OrderUpsert(order:$order) {
|
|
|
+ _id createdAt total
|
|
|
+ }
|
|
|
+ }`, {order: {orderGoods: order}})));
|
|
|
+ if(myOrder) {
|
|
|
+ dispatch(actionCartClear());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+const orders = () =>
|
|
|
+ gql(`query myOrders {
|
|
|
+ OrderFind(query:"[{}]"){
|
|
|
+ _id total orderGoods{
|
|
|
+ price count total good{
|
|
|
+ _id name images{
|
|
|
+ url
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }`, {})
|
|
|
+
|
|
|
+const actionOrders = () =>
|
|
|
+ actionPromise('myOrders', orders())
|
|
|
+
|
|
|
+const actionLogin = (login, password) =>
|
|
|
+ actionPromise('login', gql(`query log($login:String, $password:String) {
|
|
|
+ login(login:$login, password:$password)
|
|
|
+ }`, {login, password}));
|
|
|
+
|
|
|
+const actionRegister = (login, password) =>
|
|
|
+ actionPromise('register', gql(`mutation register($login:String, $password:String) {
|
|
|
+ UserUpsert(user:{login:$login, password:$password}) {
|
|
|
+ _id login createdAt
|
|
|
+ }
|
|
|
+ }`, {login, password}));
|
|
|
+
|
|
|
+const actionFullLogin = (login, password) =>
|
|
|
+ async dispatch => {
|
|
|
+ const token = await dispatch(actionLogin(login, password))
|
|
|
+ if (token){
|
|
|
+ dispatch(actionAuthLogin(token));
|
|
|
+ goToMainPage()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+const actionFullRegister = (login, password) =>
|
|
|
+ async dispatch => {
|
|
|
+ const user = await dispatch(actionRegister(login, password))
|
|
|
+ if(user) {
|
|
|
+ dispatch(actionFullLogin(login, password))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+const actionFullOrders = () =>
|
|
|
+ async dispatch => {
|
|
|
+ await dispatch(actionOrders());
|
|
|
+ if(Object.keys(store.getState().auth).length === 0) {
|
|
|
+ goToMainPage()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+function Password(parent, open) {
|
|
|
+
|
|
|
+ const input = document.createElement('input');
|
|
|
+ input.id = 'password'
|
|
|
+ input.type = 'password';
|
|
|
+ parent.appendChild(input);
|
|
|
+
|
|
|
+ const button = document.createElement('button');
|
|
|
+ button.type = 'button';
|
|
|
+ button.textContent = 'показать';
|
|
|
+ parent.appendChild(button);
|
|
|
+
|
|
|
+ button.addEventListener('click', () => {
|
|
|
+ this.setOpen(open !== true);
|
|
|
+ });
|
|
|
+
|
|
|
+ this.setValue = newValue => input.value = newValue;
|
|
|
+
|
|
|
+ this.getValue = () => input.value;
|
|
|
+
|
|
|
+ this.setOpen = openUpdate => {
|
|
|
+ open = openUpdate;
|
|
|
+ if(typeof this.onOpenChange === 'function') {
|
|
|
+ this.onOpenChange(openUpdate);
|
|
|
+ }
|
|
|
+ button.textContent = (openUpdate) ? 'показать' : 'скрыть';
|
|
|
+ input.type = (openUpdate) ? 'password' : 'text';
|
|
|
+ }
|
|
|
+
|
|
|
+ this.getOpen = () => open;
|
|
|
+
|
|
|
+ input.addEventListener('input', event => {
|
|
|
+ if (typeof this.onChange === 'function'){
|
|
|
+ this.onChange(event.target.value);
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function LoginForm(parent) {
|
|
|
+
|
|
|
+ const createDivider = () => parent.appendChild(document.createElement('br'));
|
|
|
+
|
|
|
+ const input = document.createElement('input');
|
|
|
+ input.id = 'login';
|
|
|
+ input.type = 'text';
|
|
|
+ parent.appendChild(input);
|
|
|
+
|
|
|
+ const button = document.createElement('button');
|
|
|
+ button.type = 'button';
|
|
|
+ button.textContent = 'Логин';
|
|
|
+ button.disabled = true;
|
|
|
+
|
|
|
+ input.addEventListener('input', event => {
|
|
|
+ if (typeof this.onChange === 'function'){
|
|
|
+ this.onChange(event.target.value);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ button.addEventListener('click', event => {
|
|
|
+ if (typeof this.onLogin === 'function') {
|
|
|
+ this.onLogin(event.target);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ this.getLogin = () => input.value;
|
|
|
+
|
|
|
+ createDivider();
|
|
|
+ const password = new Password(parent, true);
|
|
|
+
|
|
|
+ const getPassword = () => password.getValue();
|
|
|
+
|
|
|
+ createDivider();
|
|
|
+ parent.appendChild(button);
|
|
|
+
|
|
|
+ const isDisabled = () => button.disabled = (!(getPassword() !== '' && this.getLogin() !== ''));
|
|
|
+
|
|
|
+ password.onChange = () => isDisabled();
|
|
|
+
|
|
|
+ this.onChange = () => isDisabled();
|
|
|
+
|
|
|
+ this.setButtonText = newText => button.textContent = newText;
|
|
|
+}
|
|
|
+
|
|
|
+store.subscribe(() => {
|
|
|
+ const rootCats = store.getState().promise.rootCategories?.payload
|
|
|
+ if (rootCats){
|
|
|
+ aside.innerHTML = ''
|
|
|
+ for (let {_id, name} of rootCats){
|
|
|
+ const a = document.createElement('a')
|
|
|
+ a.innerText = name
|
|
|
+ a.href = `#/category/${_id}`
|
|
|
+ aside.append(a)
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+store.subscribe(() => {
|
|
|
+ const catById = store.getState().promise.catById?.payload
|
|
|
+ const [,route] = location.hash.split('/')
|
|
|
+ if (catById && route === 'category'){
|
|
|
+ const {name, goods, parent, subCategories} = catById;
|
|
|
+ main.innerHTML = `<h1>${name}</h1>`;
|
|
|
+
|
|
|
+ if(parent) {
|
|
|
+ const {_id, name} = parent;
|
|
|
+ const breadcrumbs = document.createElement('a');
|
|
|
+ breadcrumbs.innerText = name;
|
|
|
+ breadcrumbs.href = `#/category/${_id}`;
|
|
|
+ main.prepend(breadcrumbs);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(subCategories) {
|
|
|
+ const listSubCategories = document.createElement('ul');
|
|
|
+ for (let {_id, name} of subCategories) {
|
|
|
+ listSubCategories.innerHTML += `
|
|
|
+ <li>
|
|
|
+ <a href="#/category/${_id}">${name}</a>
|
|
|
+ </li>
|
|
|
+ `;
|
|
|
+ }
|
|
|
+ main.append(listSubCategories);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let good of goods){
|
|
|
+ const {_id, name, price, images} = good
|
|
|
+ const a = document.createElement('a')
|
|
|
+ a.classList.add('card')
|
|
|
+ a.innerHTML = `
|
|
|
+ <div>
|
|
|
+ <img class="card__image" alt="${name}" src="${backendURL + images[0]?.url}" />
|
|
|
+ <h2>${name}</h2>
|
|
|
+ <strong>${price}</strong>
|
|
|
+ </div>
|
|
|
+ `
|
|
|
+ a.href = `#/good/${_id}`
|
|
|
+
|
|
|
+ const button = document.createElement('button')
|
|
|
+ button.type = 'button';
|
|
|
+ button.innerText = 'добавить в корзину'
|
|
|
+ button.onclick = () => {
|
|
|
+ store.dispatch(actionCartAdd(good))
|
|
|
+ }
|
|
|
+ main.append(a)
|
|
|
+ main.append(button)
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+store.subscribe(() => {
|
|
|
+ const goodById = store.getState().promise.goodById?.payload
|
|
|
+ const [,route] = location.hash.split('/')
|
|
|
+ if (goodById && route === 'good'){
|
|
|
+ const {name, description, price, images} = goodById
|
|
|
+ main.innerHTML = `
|
|
|
+ <div>
|
|
|
+ <h1>${name}</h1>
|
|
|
+ <p>${description}</p>
|
|
|
+ <strong>${price}</strong>
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+ const button = document.createElement('button')
|
|
|
+ button.type = 'button';
|
|
|
+ button.innerText = 'добавить в корзину'
|
|
|
+ button.onclick = () => {
|
|
|
+ store.dispatch(actionCartAdd(goodById))
|
|
|
+ }
|
|
|
+ main.append(button);
|
|
|
+ let imageGroup = document.createElement('div');
|
|
|
+ imageGroup.classList.add('good-images')
|
|
|
+ for (const img in images) {
|
|
|
+ imageGroup.innerHTML += `
|
|
|
+ <div>
|
|
|
+ <img class="good-images__element" src="${backendURL + images[img]?.url}" alt="${name} photo-${img}">
|
|
|
+ </div>
|
|
|
+ `
|
|
|
+ }
|
|
|
+ main.append(imageGroup)
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const drawCart = () => {
|
|
|
+ const cart = store.getState().cart;
|
|
|
+ const [,route] = location.hash.split('/');
|
|
|
+ if (cart && route === 'cart'){
|
|
|
+ if(Object.keys(cart).length === 0) {
|
|
|
+ main.innerHTML = '<h1>Корзина пустая</h1>';
|
|
|
+ } else {
|
|
|
+ main.innerHTML = '';
|
|
|
+ const cartBlock = document.createElement('div');
|
|
|
+ cartBlock.classList.add('cart');
|
|
|
+ const totalAmountByPosition = [];
|
|
|
+ for (const good of Object.values(cart)) {
|
|
|
+ const {_id, name, price, images} = good.good;
|
|
|
+ totalAmountByPosition.push(price * good.amount);
|
|
|
+ const cartElement = document.createElement('div');
|
|
|
+ cartElement.classList.add('cart__element')
|
|
|
+ cartElement.innerHTML = `
|
|
|
+ <figure class="cart__figure">
|
|
|
+ <a class="cart__image-link" href="#/good/${_id}">
|
|
|
+ <img class="cart__image" src="${backendURL + images[0]?.url}" alt="${name}">
|
|
|
+ </a>
|
|
|
+ <figcaption class="cart__caption">
|
|
|
+ <a href="#/good/${_id}" class="cart__name">${name}</a>
|
|
|
+ <strong class="cart__price">Цена - ${price}</strong>
|
|
|
+ <p class="cart__amount">Количество - ${good.amount} шт.</p>
|
|
|
+ <p class="cart__amount-total">Итого по позиции - ${price * good.amount}</p>
|
|
|
+ </figcaption>
|
|
|
+ </figure>
|
|
|
+ `;
|
|
|
+ const cartQuantity = document.createElement('fieldset');
|
|
|
+ cartQuantity.classList.add('cart__quantity');
|
|
|
+ const cartQuantityAmount = document.createElement('span');
|
|
|
+ const incDecButton = (text, classStr, incDec) => {
|
|
|
+ const button = document.createElement('button');
|
|
|
+ button.type = 'button';
|
|
|
+ button.id = `${(incDec ? 'decrease' : 'increase')}`
|
|
|
+ button.innerText = text;
|
|
|
+ button.classList.add('cart__quantity-button', `cart__quantity-button--${classStr}`);
|
|
|
+ button.onclick = () => {
|
|
|
+ store.dispatch(actionCartAdd(good.good, (incDec ? -1 : +1)));
|
|
|
+ if(cart[_id].amount > 1 && button.id === 'decrease') {
|
|
|
+ button.disabled = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(cart[_id].amount === 1 && button.id === 'decrease') {
|
|
|
+ button.disabled = true;
|
|
|
+ }
|
|
|
+ return button;
|
|
|
+ }
|
|
|
+ cartQuantityAmount.classList.add('cart__quantity-amount');
|
|
|
+ cartQuantityAmount.innerText = good.amount;
|
|
|
+ cartElement.append(cartQuantity);
|
|
|
+ cartQuantity.append(incDecButton('-', 'decrease', true));
|
|
|
+ cartQuantity.append(cartQuantityAmount);
|
|
|
+ cartQuantity.append(incDecButton('+', 'increase', false));
|
|
|
+ const deleteButton = document.createElement('button');
|
|
|
+ deleteButton.type = 'button'
|
|
|
+ deleteButton.innerText = 'Удалить товар'
|
|
|
+ deleteButton.classList.add('cart__delete-button')
|
|
|
+ deleteButton.onclick = () => {
|
|
|
+ store.dispatch(actionCartDelete(good.good))
|
|
|
+ }
|
|
|
+ cartElement.append(deleteButton);
|
|
|
+ cartBlock.append(cartElement);
|
|
|
+ }
|
|
|
+ main.append(cartBlock)
|
|
|
+ const totalPrice = document.createElement('p');
|
|
|
+ totalPrice.innerHTML = `Итого - <b>${totalAmountByPosition.reduce((a, b) => a + b, 0)}</b>`;
|
|
|
+ main.append(totalPrice);
|
|
|
+ const makeOrderButton = document.createElement('button');
|
|
|
+ makeOrderButton.type = 'button';
|
|
|
+ makeOrderButton.innerText = 'Оформить заказ';
|
|
|
+ makeOrderButton.classList.add('cart-button');
|
|
|
+ makeOrderButton.onclick = () => {
|
|
|
+ store.dispatch(actionOrder());
|
|
|
+ }
|
|
|
+ main.append(makeOrderButton)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+store.subscribe(drawCart);
|
|
|
+
|
|
|
+store.subscribe(() => {
|
|
|
+ const orders = store.getState().promise.myOrders?.payload;
|
|
|
+ const [,route] = location.hash.split('/')
|
|
|
+ if(orders && route === 'orderhistory') {
|
|
|
+ main.innerHTML = '<h1>Мои заказы</h1>';
|
|
|
+ const orderCartGroup = document.createElement('div');
|
|
|
+ orderCartGroup.classList.add('order-cart-group');
|
|
|
+ for (const [index, value] of Object.entries(orders)) {
|
|
|
+ const orderCartGroupElement = document.createElement('div');
|
|
|
+ orderCartGroupElement.classList.add('order-cart-group__element')
|
|
|
+ orderCartGroupElement.innerHTML = `<h2>Заказ №${+index+1} (ID заказа - ${value._id})</h2>`;
|
|
|
+ const orderCartElements = document.createElement('div');
|
|
|
+ orderCartElements.classList.add('order-cart__elements');
|
|
|
+ const totalAll = document.createElement('p');
|
|
|
+ totalAll.innerHTML = `<p>Итого - <b>${value.total}</b></p>`;
|
|
|
+ for (const goodElement of Object.values(value.orderGoods)) {
|
|
|
+ const {price, count, total, good} = goodElement;
|
|
|
+ const orderCartElement = document.createElement('div');
|
|
|
+ orderCartElement.classList.add('order-cart__element');
|
|
|
+ orderCartElement.innerHTML = `
|
|
|
+ <figure class="order-cart__figure">
|
|
|
+ <a class="order-cart__image-link"
|
|
|
+ href="#/good/${good._id}"
|
|
|
+ >
|
|
|
+ <img class="order-cart__image"
|
|
|
+ src="${backendURL + good.images[0]?.url}"
|
|
|
+ alt="${good.name}"
|
|
|
+ >
|
|
|
+ </a>
|
|
|
+ <figcaption class="order-cart__caption">
|
|
|
+ <a class="order-cart__headline-link"
|
|
|
+ href="#/good/${good._id}"
|
|
|
+ >
|
|
|
+ <h3 class="order-cart__headline">${good.name}</h3>
|
|
|
+ </a>
|
|
|
+
|
|
|
+ <p>Цена - ${price}</p>
|
|
|
+ <p>Количество - ${count}</p>
|
|
|
+ <p>Итого по позиции - ${total}</p>
|
|
|
+ </figcaption>
|
|
|
+ </figure>
|
|
|
+ `;
|
|
|
+ orderCartElement.classList.add('order-cart__element');
|
|
|
+ orderCartElements.append(orderCartElement)
|
|
|
+ }
|
|
|
+ orderCartGroupElement.append(orderCartElements);
|
|
|
+ orderCartGroupElement.append(totalAll);
|
|
|
+ orderCartGroup.append(orderCartGroupElement)
|
|
|
+ }
|
|
|
+ main.append(orderCartGroup)
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const drawUserName = () => {
|
|
|
+ const buttonLogout = '<button onclick="store.dispatch(actionAuthLogout())" type="button">Выйти</button>';
|
|
|
+ const buttonLogin = '<a href="#/login/">Войти</a>';
|
|
|
+ const buttonRegister = '<a href="#/register/">Регистрация</a>';
|
|
|
+
|
|
|
+ authSection.innerHTML = store.getState().auth.token ? `Пользователь - <a href="#/orderhistory">${store.getState().auth.payload.sub.login}</a><div>${buttonLogout}</div>` :`Пользователь - <i>anon</i> <div>${buttonLogin} ${buttonRegister}</div>`;
|
|
|
+}
|
|
|
+drawUserName() //работаем безусловно при перезагрузке страницы
|
|
|
+store.subscribe(drawUserName) //а так же при обновлении redux
|
|
|
+
|
|
|
+// честно стырил отсюда - https://gist.github.com/realmyst/1262561?permalink_comment_id=2299442#gistcomment-2299442
|
|
|
+const declOfNum = (n, titles) => titles[(n % 10 === 1 && n % 100 !== 11) ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2];
|
|
|
+
|
|
|
+const drawCardAmount = () => {
|
|
|
+ if(store.getState().cart && store.getState().auth.token) {
|
|
|
+ let totalAmount = Object.values(store.getState().cart).reduce((a, b) => a + b.amount, 0);
|
|
|
+ cartIcon.innerHTML = `<a href="#/cart">Корзина</a> - <b>${totalAmount}</b> ${declOfNum(totalAmount, ['товар', 'товара', 'товаров'])}`;
|
|
|
+ }
|
|
|
+}
|
|
|
+drawCardAmount()
|
|
|
+store.subscribe(drawCardAmount)
|
|
|
+
|
|
|
+store.dispatch(actionAuthLogin(localStorage.authToken))
|
|
|
+store.dispatch(actionRootCategories())
|
|
|
+
|
|
|
+//#/category/АЙДИШНИК
|
|
|
+//#/good/АЙДИШНИК
|
|
|
+window.onhashchange = () => {
|
|
|
+ const [,route, _id] = location.hash.split('/')
|
|
|
+
|
|
|
+ const routes = {
|
|
|
+ category() {
|
|
|
+ store.dispatch(actionCategoryById(_id))
|
|
|
+ },
|
|
|
+ good(){
|
|
|
+ store.dispatch(actionGoodById(_id))
|
|
|
+ },
|
|
|
+ login(){
|
|
|
+ main.innerHTML = '';
|
|
|
+ const loginForm = new LoginForm(main);
|
|
|
+ loginForm.onLogin = () => store.dispatch(actionFullLogin(login.value, password.value))
|
|
|
+ },
|
|
|
+ register(){
|
|
|
+ main.innerHTML = '';
|
|
|
+ const registerForm = new LoginForm(main);
|
|
|
+ registerForm.setButtonText('Регистрация');
|
|
|
+ registerForm.onLogin = () => store.dispatch(actionFullRegister(login.value, password.value));
|
|
|
+ },
|
|
|
+ cart(){
|
|
|
+ drawCart();
|
|
|
+ },
|
|
|
+ orderhistory(){
|
|
|
+ store.dispatch(actionFullOrders())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (route in routes){
|
|
|
+ routes[route]()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+window.onhashchange()
|