123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Magaz</title>
- <style>
- * *,
- *::before,
- *::after {
- box-sizing: border-box;
- margin: 0;
- padding: 0;
- }
- #page-wrapper {
- display: flex;
- flex-direction: column;
- min-height: 100vh;
- margin: 0 auto;
- max-width: 1170px;
- width: 100%;
- }
- #mainContainer {
- display: flex;
- flex-grow: 1;
- margin: 20px 0;
- }
- #header {
- height: 50px;
- background-color: #FF5319;
- border: 1px solid gray;
- padding: 10px 5px;
- font-size: 26px;
- display: flex;
- justify-content: space-between;
- }
- #userAuth li {
- list-style-type: none;
- display: inline-block;
- font-size: 18px;
- }
- a {
- text-decoration: none;
- }
- #aside {
- min-width: 25%;
- font-size: 20px;
- padding: 0 20px 0px 0;
- }
- #aside>a {
- display: block;
- color: black;
- }
- #aside>a:hover {
- color: tomato;
- font-size: 24px;
- }
- #main {
- display: flex;
- flex-wrap: wrap;
- width: 100%;
- }
- #footer {
- text-align: center;
- background-color: #FF5319;
- border: 1px solid gray;
- height: 40px;
- }
- #cartImg {
- height: 30px;
- width: 30px;
- }
- #nav {
- display: flex;
- }
- </style>
- </head>
- <body>
- <div id="page-wrapper">
- <header id="header">
- <div id="logo">Larek</div>
- <nav id="nav">
- <ul id="userAuth">
- <li>войти</li>
- <li>регистрация</li>
- </ul>
- <div id="cart">
- <a id="cartLink"><img id="cartImg"
- src="https://i.pinimg.com/originals/15/4f/df/154fdf2f2759676a96e9aed653082276.png"
- alt=""></a>
- </div>
- </nav>
- </header>
- <div id='mainContainer'>
- <aside id='aside'>
- Категории
- </aside>
- <main id='main'>
- Контент
- </main>
- </div>
- <footer id="footer">КУДА Я ПОПАЛ?2</footer>
- </div>
- <script>
- // debugger;
- 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
- }
- }
- }
- // function reducer(state = {}, { type, status, payload, error, name }) {
- // if (type === 'PROMISE') {
- // return {
- // ...state,
- // [name]: { status, payload, error }
- // }
- // }
- // return state
- // }
- /////
- function promiseReducer(state = {}, { type, status, payload, error, name }) {
- if (type === 'PROMISE') {
- return {
- ...state,
- [name]: { status, payload, error }
- }
- }
- return state
- }
- function cardReducer(state = {}, { type, count = 1, good }) {
- //придумать типы CART_ADD, CART_CLEAR, CART_SET, CART_DELETE
- //{ _id1: {count: 2, good: {....}},
- // _id2: 10,}
- //
- if (type === 'CART_ADD') { ///где то тут проеб
- console.log("+1")
- const _id = good._id
- return {
- ...state,
- [_id]: { count: (state[_id]?.count || 0) + count, good }
- }
- }
- if (type === 'CART_DELETE') {
- console.log("-1")
- const _id = good._id
- return {
- ...state,
- [_id]: { count: (state[_id]?.count || 0) - count, good }
- }
- }
- return state
- }
- const actionCartAdd = (good, count = 1) => ({ type: 'CART_ADD', count, good })
- // const actionCartDelete = (good, count = 1) => ({ type: 'CART_DELETE', count, good })
- //под товаром сделать кнопку "купить"
- // buy.onclick = () => {
- // //debugger;
- // store.dispatch(actionCartAdd(good))
- // }
- //отрисовка количество где-то в хидере (на всех страницах)
- // store.subscribe(() => {
- // const cart = store.getState().cart
- // смочь в цикл/reduce по подсчету суммы количеств товаров и вывести куда-то в дом
- //(заготовка под кошик с количеством)
- // })
- function authReducer(state = { name: "Токен и все такое" }, action) { // { type, token }
- if (state === undefined) {
- //добавить в action token из localStorage, и проимитировать LOGIN (action.type = 'LOGIN')
- return {}
- }
- if (action.type === 'LOGIN') {
- console.log('ЛОГИН', action)
- //+localStorage
- //jwt_decode:
- //достать среднюю часть из токена (между точками)
- //atob
- //JSON.parse
- // return {token: action.token, payload: jwt_decode(action.jwt)}
- }
- if (action.type === 'LOGOUT') {
- console.log('ЛОГАУТ')
- //-localStorage
- //removeItem или clear
- //вернуть пустой объект
- return {}
- }
- return state
- }
- const reducers = {
- promise: promiseReducer,
- cart: cardReducer,
- auth: authReducer
- }
- const combineReducers = reducers => {
- return (state = {}, action) => {
- const newState = {}
- for (const [name, reducer] of Object.entries(reducers)) {
- // console.log(name, reducer)
- const newSubState = reducer(state[name], action)
- if (newSubState !== state[name]) {
- newState[name] = newSubState
- }
- }
- if (Object.keys(newState).length === 0) {
- return state
- }
- // {
- // ...
- // promise: state.promise,
- // cart: state.cart,
- // auth: state.auth,
- // promise: newState.promise
- // }
- return { ...state, ...newState }
- }
- }
- const store = createStore(combineReducers(reducers))
- ////
- //const store = createStore(reducer)
- const unsubscribe1 = store.subscribe(() => console.log(store.getState()))
- 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 delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
- //store.dispatch(actionPending('delay1000'))
- //delay(1000).then(payload => store.dispatch(actionResolved('delay1000', payload)),
- //error => store.dispatch(actionRejected('delay1000', 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 getGQL = url =>
- (query, variables = {}) => fetch(url, {
- method: 'POST',
- headers: {
- // Accept: "application/json",
- "Content-Type": "application/json"
- //якщо в localStorage.authToken шото есть, то наверное это надо отправить с заголовком Authorization
- },
- body: JSON.stringify({ query, variables })
- }).then(res => res.json())
- let shopGQL = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql')
- const actionRootCategories = () =>
- actionPromise('rootCategories', shopGQL(`
- query cats($query:String){
- CategoryFind(query:$query){
- _id name
- }
- }
- `, { query: JSON.stringify([{ parent: null }]) }))
- const actionCategoryById = (_id) =>
- actionPromise('catById', shopGQL(`query catById($query:String){
- CategoryFindOne(query:$query){
- _id name goods{
- _id name price description images{
- url
- }
- }
- }
- }`, { query: JSON.stringify([{ _id }]) }))
- const actionGoodById = (_id) =>
- actionPromise('goodById', shopGQL(`query goodById($query:String){
- GoodFindOne(query:$query){
- _id name price description images {
- url
- }
- }
- }`, { query: JSON.stringify([{ _id }]) }))
- const actionGetToken = (login, password) =>
- actionPromise('getToken', shopGQL(`query login($login:String, $password:String){
- login(login: $login, password: $password)
- }`, { login, password }))
- // const actionGetOrders = () =>
- // actionPromise('orders', shopGQL(`query orders{
- // OrderFind(query:"[{}]"){
- // _id total orderGoods{
- // count good{
- // name
- // }
- // }
- // }
- // }`))
- const actionAuthLogin = token => ({ type: 'LOGIN', token })
- const actionFullLogin = (login, password) =>
- async dispatch => {
- let payload = await dispatch(actionGetToken(login, password))
- if (payload.data.login) {
- dispatch(actionAuthLogin(payload.data.login))
- }
- }
- //НАПИЛИТЬ actionFullRegister:
- // - actionRegister, который actionPromise
- // - если удачно, делаете сразу же actionFullLogin
- store.dispatch(actionRootCategories())
- store.dispatch(actionFullLogin('tst123', '123123'))
- function drawCart() {
- //цикл по отрисовке с картинками и редактирование количества/удалением товара
- //
- const cart = store.getState().cart
- // if (cart) {
- // main.innerText = ''
- // for (let {})
- // }
- // main.innerHTML = `<pre>${JSON.stringify(cart, null, 4)}</pre>`
- console.log("kart", cart)
- }
- function loadAnimationFunc() {
- main.innerHTML = ""
- let loadAnimationContainer = document.createElement('div')
- loadAnimationContainer.setAttribute('style', "width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;")
- main.append(loadAnimationContainer)
- let loadAnimation = document.createElement('img')
- loadAnimation.src = "https://image.flaticon.com/icons/png/512/2492/2492765.png"
- loadAnimation.setAttribute("style", "width: 50px; height: 50px; animation: load 1s linear infinite;")
- loadAnimation.animate([{ transform: 'rotate(360deg)' }], { duration: 1000, iterations: Infinity })
- loadAnimationContainer.append(loadAnimation)
- }
- window.onhashchange = () => {
- let { 1: route, 2: id } = location.hash.split('/')
- if (route === 'categories') {
- loadAnimationFunc()
- store.dispatch(actionCategoryById(id))
- }
- if (route === 'good') {
- loadAnimationFunc()
- store.dispatch(actionGoodById(id))
- }
- if (route === 'login') {
- // нарисовать форму логина, которая по OK делает
- // store.dispatch(actionFullLogin(login, password))
- }
- if (route === 'register') {
- // нарисовать форму регистрации, которая по OK делает
- // store.dispatch(actionFullRegister(login, password))
- }
- if (route === 'cart') {
- loadAnimationFunc()
- // store.dispatch(actionGetOrders())
- console.log("страница корзины")
- drawCart()
- //#/cart/
- // //нарисовать корзину с кнопочками добавления/удаления товаров
- // main.innerHTML = ""
- // //const cart = store.getState().cart
- // //смочь в цикл / reduce по подсчету суммы количеств товаров и вывести куда - то в дом
- // //(заготовка под кошик с количеством)
- }
- }
- function drawMainMenu() {
- //debugger;
- 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
- console.log("cart")
- cartLink.href = `#/cart/`
- aside.append(catA)
- }
- }
- }
- store.subscribe(drawMainMenu)
- store.subscribe(() => {
- //debugger;
- const { 1: route, 2: id } = location.hash.split('/')
- if (route === 'categories') {
- const catById = store.getState().promise.catById?.payload
- if (catById) {
- main.innerText = ""
- // Вывести категорию(название)
- let h1 = document.createElement('h1')
- h1.setAttribute('style', 'width: 100%;')
- h1.innerText = catById.data.CategoryFindOne.name
- main.append(h1)
- // Вывести циклом товары со ссылками вида # / good / АЙДИШНИК
- for (let { _id, name, description, price, images } of catById.data.CategoryFindOne.goods) { //тута
- let divGoodA = document.createElement('div')
- divGoodA.setAttribute('style', "margin: 0 10px 10px 0; padding: 20px; width: 400px; border: 2px solid gray; display: flex; flex-direction: column; align-content: stretch ")
- divGoodA.onmousemove = () => divGoodA.style.borderColor = "#FF7373"
- divGoodA.onmouseout = () => divGoodA.style.borderColor = "gray"
- //background - color: #FF5319;
- let goodImg = document.createElement('img')
- goodImg.src = `http://shop-roles.asmer.fs.a-level.com.ua/${images[0].url}`
- let goodLink = document.createElement('a')
- goodLink.setAttribute('style', "flex: 1 1 auto; text-align: center; font-size: 22px")
- goodLink.href = `#/good/${_id}`
- goodLink.innerHTML = name
- let goodPrice = document.createElement('div')
- goodPrice.setAttribute('style', "font-size: 20px; font-weight: bold; text-align: center;")
- goodPrice.innerText = `Цена: ${price} грн`
- let goodButton = document.createElement('button')
- goodButton.setAttribute('style', "height: 50px; font-weight: bold; margin: 10px 0 0 0; border-radius: 30px; background-color: white;")
- goodButton.innerText = "Купить"
- goodButton.onclick = () => window.location.href = `#/good/${_id}`
- goodButton.onmousemove = () => goodButton.style.backgroundColor = "#FF5319"
- goodButton.onmouseout = () => goodButton.style.backgroundColor = "white"
- divGoodA.append(goodImg)
- divGoodA.append(goodLink)
- //divGoodA.append(goodDescription)
- divGoodA.append(goodPrice)
- divGoodA.append(goodButton)
- main.append(divGoodA)
- }
- // main.innerHTML = `<pre>${JSON.stringify(catById, null, 4)}</pre>`
- }
- }
- if (route === 'cart') {
- // нарисовать корзину с кнопочками добавления/удаления товаров
- //
- //const cart = store.getState().cart
- drawCart()
- }
- })
- store.subscribe(() => {
- // debugger;
- //когда появится actionGoodById и ссылки на товары это заработает
- const { 1: route, 2: id } = location.hash.split('/')
- if (route === 'good') {
- const goodById = store.getState().promise.goodById?.payload
- if (goodById) {
- let main = document.getElementById('main')
- main.innerHTML = ""
- let divGoodB = document.createElement('div')
- divGoodB.setAttribute('style', "margin: 0 10px 10px 25px; padding: 20px; display: flex; flex-direction: column;")
- let goodImg1 = document.createElement('img')
- goodImg1.src = `http://shop-roles.asmer.fs.a-level.com.ua/${goodById.data.GoodFindOne.images[0].url}`
- let goodLink1 = document.createElement('h1')
- goodLink1.setAttribute('style', "text-align: center; font-size: 22px")
- goodLink1.innerHTML = goodById.data.GoodFindOne.name
- let goodDescription1 = document.createElement('div')
- goodDescription1.setAttribute('style', "margin: 20px; ")
- goodDescription1.innerText = goodById.data.GoodFindOne.description
- let goodPrice1 = document.createElement('h1')
- goodPrice1.setAttribute('style', "font-size: 20px; font-weight: bold; text-align: center;")
- goodPrice1.innerText = `Цена: ${goodById.data.GoodFindOne.price} грн`
- let goodButton1 = document.createElement('button')
- goodButton1.setAttribute('style', "height: 50px; font-weight: bold; margin: 10px 0 0 0; border-radius: 30px; background-color: white;")
- goodButton1.innerText = "Купить"
- goodButton1.onmousemove = () => goodButton1.style.backgroundColor = "#FF5319"
- goodButton1.onmouseout = () => goodButton1.style.backgroundColor = "white"
- goodButton1.onclick = () => { store.dispatch(actionCartAdd(goodById.data.GoodFindOne)) }
- divGoodB.append(goodImg1)
- divGoodB.append(goodLink1)
- divGoodB.append(goodDescription1)
- divGoodB.append(goodPrice1)
- divGoodB.append(goodButton1)
- main.append(divGoodB)
- // вывести в main страницу товаров
- }
- }
- })
- </script>
- </body>
- </html>
|