|
@@ -58,11 +58,52 @@ const actionAuthLogin = (token) => ({
|
|
|
});
|
|
|
const actionAuthLogout = () => ({ type: "AUTH_LOGOUT" });
|
|
|
|
|
|
-function promiseReducer(state = {}, { type, status, payload, error, name }) {
|
|
|
+function cartReducer(state = {}, { type, good = {}, count }) {
|
|
|
+ const { _id } = good;
|
|
|
+
|
|
|
+ const types = {
|
|
|
+ CART_ADD() {
|
|
|
+ count = +count;
|
|
|
+ if (!count) {
|
|
|
+ return state;
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ [_id]: { good, count: count + (state[_id]?.count || 0) },
|
|
|
+ };
|
|
|
+ },
|
|
|
+ CART_CHANGE() {
|
|
|
+ count = +count;
|
|
|
+ if (!count) {
|
|
|
+ return state;
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ [_id]: { good, count },
|
|
|
+ };
|
|
|
+ },
|
|
|
+ CART_REMOVE() {
|
|
|
+ let { [_id]: remove, ...goods } = state;
|
|
|
+ return goods;
|
|
|
+ },
|
|
|
+ CART_CLEAR() {
|
|
|
+ return {};
|
|
|
+ },
|
|
|
+ };
|
|
|
+ if (type in types) {
|
|
|
+ return types[type]();
|
|
|
+ }
|
|
|
+ return state;
|
|
|
+}
|
|
|
+
|
|
|
+function promiseReducer(state = {}, { type, status, payload, errors, name }) {
|
|
|
+ if (!state) {
|
|
|
+ return {};
|
|
|
+ }
|
|
|
if (type === "PROMISE") {
|
|
|
return {
|
|
|
...state,
|
|
|
- [name]: { status, payload, error },
|
|
|
+ [name]: { status, payload, errors },
|
|
|
};
|
|
|
}
|
|
|
return state;
|
|
@@ -74,11 +115,11 @@ const actionResolved = (name, payload) => ({
|
|
|
name,
|
|
|
payload,
|
|
|
});
|
|
|
-const actionRejected = (name, error) => ({
|
|
|
+const actionRejected = (name, errors) => ({
|
|
|
type: "PROMISE",
|
|
|
status: "REJECTED",
|
|
|
name,
|
|
|
- error,
|
|
|
+ errors,
|
|
|
});
|
|
|
const actionPromise = (name, promise) => async (dispatch) => {
|
|
|
dispatch(actionPending(name));
|
|
@@ -112,9 +153,48 @@ function combineReducers(reducers) {
|
|
|
const combinedReducer = combineReducers({
|
|
|
promise: promiseReducer,
|
|
|
auth: authReducer,
|
|
|
+ cart: cartReducer,
|
|
|
});
|
|
|
const store = createStore(combinedReducer);
|
|
|
|
|
|
+const actionCartAdd = (good, count) => ({
|
|
|
+ type: "CART_ADD",
|
|
|
+ good,
|
|
|
+ count,
|
|
|
+});
|
|
|
+const actionCartChange = (good, count) => ({
|
|
|
+ type: "CART_CHANGE",
|
|
|
+ good,
|
|
|
+ count,
|
|
|
+});
|
|
|
+const actionCartRemove = (good) => ({ type: "CART_REMOVE", good });
|
|
|
+const actionCartClear = () => ({ type: "CART_CLEAR" });
|
|
|
+
|
|
|
+const actionOrder = () => async (dispatch, getState) => {
|
|
|
+ let { cart } = getState();
|
|
|
+ let orderGoods = Object.entries(cart).map(([, { _id, count }]) => ({
|
|
|
+ good: { _id, count },
|
|
|
+ }));
|
|
|
+ dispatch(
|
|
|
+ actionPromise(
|
|
|
+ "order",
|
|
|
+ gql(`
|
|
|
+ mutation newOrder($order:OrderInput){
|
|
|
+ OrderUpsert(order:$order)
|
|
|
+ { _id total }
|
|
|
+ }
|
|
|
+ `),
|
|
|
+ { order: { orderGoods } }
|
|
|
+ )
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+// store.dispatch(actionCartAdd({ _id: "beer" }, 1));
|
|
|
+// store.dispatch(actionCartAdd({ _id: "vodka" }, 1));
|
|
|
+// store.dispatch(actionCartChange({ _id: "beer" }, 10));
|
|
|
+// store.dispatch(actionCartRemove({ _id: "vodka" }));
|
|
|
+// console.log(store.getState());
|
|
|
+
|
|
|
const getGQL =
|
|
|
(url) =>
|
|
|
async (query, variables = {}) => {
|
|
@@ -182,6 +262,45 @@ const actionGoodById = (_id) =>
|
|
|
)
|
|
|
);
|
|
|
|
|
|
+const actionLogin = (login, password) =>
|
|
|
+ actionPromise(
|
|
|
+ "login",
|
|
|
+ gql(
|
|
|
+ `query log($login: String, $password: String) {
|
|
|
+ login(login: $login, password: $password)
|
|
|
+ }`,
|
|
|
+ { login: login, password: password }
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+const actionFullLogin = (login, password) => async (dispatch) => {
|
|
|
+ let token = await dispatch(actionLogin(login, password));
|
|
|
+ if (token) {
|
|
|
+ dispatch(actionAuthLogin(token));
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const actionRegister = (login, password) =>
|
|
|
+ actionPromise(
|
|
|
+ "register",
|
|
|
+ gql(
|
|
|
+ `mutation reg($user:UserInput) {
|
|
|
+ UserUpsert(user:$user) {
|
|
|
+ _id
|
|
|
+ }
|
|
|
+ }
|
|
|
+ `,
|
|
|
+ { user: { login: login, password: password } }
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+const actionFullRegister = (login, password) => async (dispatch) => {
|
|
|
+ let check = await dispatch(actionRegister(login, password));
|
|
|
+ if (check) {
|
|
|
+ dispatch(actionFullLogin(login, password));
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
store.dispatch(actionRootCats());
|
|
|
store.dispatch(actionGoodById());
|
|
|
|
|
@@ -204,6 +323,10 @@ store.subscribe(() => {
|
|
|
|
|
|
window.onhashchange = () => {
|
|
|
const [, route, _id] = location.hash.split("/");
|
|
|
+ let signOut = document.createElement("button");
|
|
|
+ signOut.innerText = "Sign Out";
|
|
|
+ signOut.id = "signoutBtn";
|
|
|
+ signOut.className = "btn btn-secondary";
|
|
|
const routes = {
|
|
|
category() {
|
|
|
store.dispatch(actionCatById(_id));
|
|
@@ -211,6 +334,42 @@ window.onhashchange = () => {
|
|
|
good() {
|
|
|
store.dispatch(actionGoodById(_id));
|
|
|
},
|
|
|
+ login() {
|
|
|
+ let loginInput = document.getElementById("loginInput");
|
|
|
+ let passwordInput = document.getElementById("pass1Input");
|
|
|
+ if (
|
|
|
+ loginInput &&
|
|
|
+ passwordInput &&
|
|
|
+ loginInput.value &&
|
|
|
+ passwordInput.value
|
|
|
+ ) {
|
|
|
+ store.dispatch(
|
|
|
+ actionFullLogin(
|
|
|
+ loginInput.value.slice(0, loginInput.value.indexOf("@")),
|
|
|
+ passwordInput.value
|
|
|
+ )
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ throw new Error("Error login");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ register() {
|
|
|
+ let regInput = document.getElementById("regInput");
|
|
|
+ let passwordInput = document.getElementById("pass2Input");
|
|
|
+ if (regInput && passwordInput && regInput.value && passwordInput.value) {
|
|
|
+ store.dispatch(
|
|
|
+ actionFullRegister(
|
|
|
+ regInput.value.slice(0, regInput.value.indexOf("@")),
|
|
|
+ passwordInput.value
|
|
|
+ )
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ throw new Error("Error register");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ cart() {
|
|
|
+ // Сделать страницу с позициями, полями ввода количества, картинками
|
|
|
+ },
|
|
|
};
|
|
|
if (route in routes) routes[route]();
|
|
|
};
|
|
@@ -239,8 +398,8 @@ store.subscribe(() => {
|
|
|
}
|
|
|
}
|
|
|
if (promise.catById.payload?.goods) {
|
|
|
- for (const { _id, name, price, images } of promise.catById.payload
|
|
|
- .goods) {
|
|
|
+ for (const good of promise.catById.payload.goods) {
|
|
|
+ const { _id, name, price, images } = good;
|
|
|
const card = document.createElement("div");
|
|
|
const link = document.createElement("a");
|
|
|
card.innerHTML = `<h2>${name}</h2>
|
|
@@ -250,14 +409,33 @@ store.subscribe(() => {
|
|
|
link.innerText = name;
|
|
|
link.href = `#/good/${_id}`;
|
|
|
card.appendChild(link);
|
|
|
+
|
|
|
+ let buyBtn = document.createElement("button");
|
|
|
+ buyBtn.className = "btn btn-success";
|
|
|
+ buyBtn.textContent = "Buy";
|
|
|
+ buyBtn.onclick = () => {
|
|
|
+ store.dispatch(actionCartAdd(good, 1));
|
|
|
+ };
|
|
|
+ card.appendChild(buyBtn);
|
|
|
main.append(card);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+store.subscribe(() => {
|
|
|
+ const { cart } = store.getState();
|
|
|
+ let countGoods = 0;
|
|
|
+
|
|
|
+ for (let good of Object.keys(cart)) {
|
|
|
+ countGoods += cart[good].count;
|
|
|
+ }
|
|
|
+ cartIcon.innerText = `Товаров в корзине: ${countGoods}`;
|
|
|
+});
|
|
|
+
|
|
|
store.subscribe(() => {
|
|
|
const { promise } = store.getState();
|
|
|
+
|
|
|
const [, route, _id] = location.hash.split("/");
|
|
|
if (promise?.goodById?.payload && route === "good") {
|
|
|
main.innerHTML = "";
|
|
@@ -286,85 +464,42 @@ function jwtDecode(token) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const actionLogin = (login, password) =>
|
|
|
- actionPromise(
|
|
|
- "login",
|
|
|
- gql(
|
|
|
- `query login($login: String, $password: String){
|
|
|
- login(login: $login, password: $password)
|
|
|
- }`,
|
|
|
- { login: login, password: password }
|
|
|
- )
|
|
|
- );
|
|
|
-
|
|
|
-const actionFullLogin = (login, password) => {
|
|
|
- async (dispatch) => {
|
|
|
- let token = await dispatch(actionLogin(login, password));
|
|
|
- if (token) {
|
|
|
- dispatch(actionAuthLogin(token));
|
|
|
- }
|
|
|
- return token;
|
|
|
- };
|
|
|
-};
|
|
|
-
|
|
|
-const actionRegister = (login, password) =>
|
|
|
- actionPromise(
|
|
|
- "register",
|
|
|
- gql(
|
|
|
- `mutation register($login:String, $password: String){
|
|
|
- UserUpsert(user:{
|
|
|
- login: $login,
|
|
|
- password: $password,
|
|
|
- nick: $login}){
|
|
|
- _id login
|
|
|
- }
|
|
|
- }`,
|
|
|
- { login: login, password: password }
|
|
|
- )
|
|
|
- );
|
|
|
-
|
|
|
-const actionFullRegister = (login, password) => async (dispatch) => {
|
|
|
- let check = await dispatch(actionRegister(login, password));
|
|
|
- if (check) {
|
|
|
- let token = await dispatch(actionLogin(login, password));
|
|
|
- if (token) {
|
|
|
- dispatch(actionAuthLogin(token));
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
+store.subscribe(async () => {
|
|
|
+ const { promise } = store.getState();
|
|
|
|
|
|
-let count = 0;
|
|
|
+ let signinBtn = document.getElementById("signinBtn");
|
|
|
+ let signupBtn = document.getElementById("signupBtn");
|
|
|
|
|
|
-store.subscribe(() => {
|
|
|
- const { auth, promise } = store.getState();
|
|
|
- count++;
|
|
|
- if (!auth?.payload && count === 1) {
|
|
|
+ if (promise?.rootCats?.payload && !signinBtn && !signupBtn) {
|
|
|
let signIn = document.createElement("button");
|
|
|
signIn.innerText = "Sign In";
|
|
|
signIn.id = "signinBtn";
|
|
|
+ signIn.type = "button";
|
|
|
signIn.className = "btn btn-light";
|
|
|
let signUp = document.createElement("button");
|
|
|
signUp.innerText = "Sign Up";
|
|
|
signUp.id = "signupBtn";
|
|
|
+ signUp.type = "button";
|
|
|
signUp.className = "btn btn-light";
|
|
|
let authBlock = document.createElement("div");
|
|
|
authBlock.id = "authBlock";
|
|
|
authBlock.appendChild(signIn);
|
|
|
authBlock.appendChild(signUp);
|
|
|
authnav.appendChild(authBlock);
|
|
|
- let signOut = document.createElement("button");
|
|
|
- signOut.innerText = "Sign Out";
|
|
|
- signOut.id = "signoutBtn";
|
|
|
- signOut.className = "btn btn-secondary";
|
|
|
+ }
|
|
|
+});
|
|
|
|
|
|
- signIn.onclick = () => {
|
|
|
+store.subscribe(async () => {
|
|
|
+ let signinBtn = document.getElementById("signinBtn");
|
|
|
+ if (signinBtn) {
|
|
|
+ signinBtn.onclick = () => {
|
|
|
try {
|
|
|
let registerFields = document.getElementById("registerFields");
|
|
|
if (registerFields !== null) {
|
|
|
registerFields.style.display = "none";
|
|
|
}
|
|
|
- signIn.style.display = "none";
|
|
|
- signUp.style.display = "inline";
|
|
|
+ signinBtn.style.display = "none";
|
|
|
+ signupBtn.style.display = "inline";
|
|
|
|
|
|
let fieldsBlock = document.createElement("div");
|
|
|
fieldsBlock.className = "fields";
|
|
@@ -388,36 +523,29 @@ store.subscribe(() => {
|
|
|
fieldsBlock.appendChild(login);
|
|
|
authBlock.prepend(fieldsBlock);
|
|
|
|
|
|
- let loginBtn = document.getElementById("loginBtn");
|
|
|
- loginBtn.onclick = async () => {
|
|
|
- let loginInput = document.getElementById("loginInput");
|
|
|
- let passwordInput = document.getElementById("pass1Input");
|
|
|
- if (loginInput.value && passwordInput.value) {
|
|
|
- store.dispatch(
|
|
|
- actionFullLogin(loginInput.value, passwordInput.value)
|
|
|
- );
|
|
|
- if (actionFullLogin(loginInput.value, passwordInput.value)) {
|
|
|
- let loginBlock = document.getElementById("loginFields");
|
|
|
- loginBlock.style.display = "none";
|
|
|
- signupBtn.style.display = "none";
|
|
|
- authBlock.appendChild(signOut);
|
|
|
- }
|
|
|
- }
|
|
|
+ loginBtn.onclick = () => {
|
|
|
+ window.location.href = "#/login";
|
|
|
console.log(store.getState());
|
|
|
};
|
|
|
} catch (error) {
|
|
|
console.log(error);
|
|
|
}
|
|
|
};
|
|
|
+ }
|
|
|
+});
|
|
|
|
|
|
- signUp.onclick = () => {
|
|
|
+store.subscribe(async () => {
|
|
|
+ let signupBtn = document.getElementById("signupBtn");
|
|
|
+ if (signupBtn) {
|
|
|
+ signupBtn.onclick = () => {
|
|
|
try {
|
|
|
let loginFields = document.getElementById("loginFields");
|
|
|
if (loginFields !== null) {
|
|
|
loginFields.style.display = "none";
|
|
|
}
|
|
|
- signIn.style.display = "inline";
|
|
|
- signUp.style.display = "none";
|
|
|
+ signinBtn.style.display = "inline";
|
|
|
+ signupBtn.style.display = "none";
|
|
|
+
|
|
|
let fieldsBlock = document.createElement("div");
|
|
|
fieldsBlock.className = "fields";
|
|
|
fieldsBlock.id = "registerFields";
|
|
@@ -440,44 +568,63 @@ store.subscribe(() => {
|
|
|
fieldsBlock.appendChild(register);
|
|
|
authBlock.prepend(fieldsBlock);
|
|
|
|
|
|
- let regBtn = document.getElementById("regBtn");
|
|
|
- regBtn.onclick = async () => {
|
|
|
- let regInput = document.getElementById("regInput");
|
|
|
- let passwordInput = document.getElementById("pass2Input");
|
|
|
- if (regInput.value && passwordInput.value) {
|
|
|
- store.dispatch(
|
|
|
- actionFullRegister(regInput.value, passwordInput.value)
|
|
|
- );
|
|
|
- if (actionFullRegister(regInput.value, passwordInput.value)) {
|
|
|
- let registerBlock = document.getElementById("registerFields");
|
|
|
- registerBlock.style.display = "none";
|
|
|
- signinBtn.style.display = "none";
|
|
|
- authBlock.appendChild(signOut);
|
|
|
- let nickname = regInput.value.slice(
|
|
|
- 0,
|
|
|
- regInput.value.indexOf("@")
|
|
|
- );
|
|
|
- let account = document.createElement("button");
|
|
|
- account.textContent = nickname;
|
|
|
- account.type = "button";
|
|
|
- account.id = "accountLink";
|
|
|
- account.className = "btn btn-success";
|
|
|
- authBlock.prepend(account);
|
|
|
- }
|
|
|
- }
|
|
|
+ regBtn.onclick = () => {
|
|
|
+ window.location.href = "#/register";
|
|
|
console.log(store.getState());
|
|
|
};
|
|
|
} catch (error) {
|
|
|
console.log(error);
|
|
|
}
|
|
|
};
|
|
|
+ }
|
|
|
+});
|
|
|
|
|
|
+store.subscribe(async () => {
|
|
|
+ const { promise, auth } = store.getState();
|
|
|
+ let accountLink = document.getElementById("accountLink");
|
|
|
+ let signoutBtn = document.getElementById("signoutBtn");
|
|
|
+ let signinBtn = document.getElementById("signinBtn");
|
|
|
+ let signupBtn = document.getElementById("signupBtn");
|
|
|
+
|
|
|
+ if (auth?.token && !signoutBtn && !accountLink) {
|
|
|
+ signinBtn.style.display = "none";
|
|
|
+ signupBtn.style.display = "none";
|
|
|
+
|
|
|
+ if (promise?.register?.status === "RESOLVED") {
|
|
|
+ let registerBlock = document.getElementById("registerFields");
|
|
|
+ registerBlock.style.display = "none";
|
|
|
+ signupBtn.style.display = "none";
|
|
|
+ window.location.href = window.location.href.replace("#/register", "");
|
|
|
+ console.log(store.getState());
|
|
|
+ }
|
|
|
+ if (promise?.login?.status === "RESOLVED") {
|
|
|
+ let loginBlock = document.getElementById("loginFields");
|
|
|
+ loginBlock.style.display = "none";
|
|
|
+ signinBtn.style.display = "none";
|
|
|
+ window.location.href = window.location.href.replace("#/login", "");
|
|
|
+ console.log(store.getState());
|
|
|
+ }
|
|
|
+ let signOut = document.createElement("button");
|
|
|
+ signOut.innerText = "Sign Out";
|
|
|
+ signOut.id = "signoutBtn";
|
|
|
+ signOut.className = "btn btn-secondary";
|
|
|
+ let authBlock = document.createElement("div");
|
|
|
+ authBlock.id = "authBlock";
|
|
|
+ authBlock.appendChild(signOut);
|
|
|
+ let nickname = auth.payload.sub.login;
|
|
|
+ let account = document.createElement("button");
|
|
|
+ account.textContent = nickname;
|
|
|
+ account.type = "button";
|
|
|
+ account.id = "accountLink";
|
|
|
+ account.className = "btn btn-success";
|
|
|
+ authBlock.prepend(account);
|
|
|
+ authnav.appendChild(authBlock);
|
|
|
signOut.onclick = () => {
|
|
|
signOut.style.display = "none";
|
|
|
store.dispatch(actionAuthLogout());
|
|
|
- signIn.style.display = "inline";
|
|
|
- signUp.style.display = "inline";
|
|
|
- accountLink.remove();
|
|
|
+ signinBtn.style.display = "inline";
|
|
|
+ signupBtn.style.display = "inline";
|
|
|
+ account.remove();
|
|
|
};
|
|
|
}
|
|
|
});
|