123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 |
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>Korzinap!!!!!!!!!!!!!!!</title>
- <style>
- .Jorik{
- border-bottom: 1px dotted green;
- }
- #content {
- display: flex;
- }
- #aside {
- width: 30%;
- }
- #aside > a {
- display: block;
- }
- .wrapper{
- border: 1px dotted green;
- }
- img{
- width:300px;
- }
- main{
- padding-left: 20px;
- }
- table{
- border:1px solid black;
- }
- td{
- text-align: center;
- border:1px solid black;
- }
-
- header {
- display: flex;
- justify-content: space-between;
- }
- #ownCab, .wrapper{
- display: flex;
- justify-content: space-between;
- }
- #ownCab{
- margin-left: 1em;
- }
- #ownCab a, button{
- margin-left: 1em;
- }
- .cart img{
- width: 1.5em;
- height: 1.5em;
- }
- a {
- text-decoration: none;
- color:blue;
- }
- a:hover{
- color: green;
- }
- </style>
- </head>
- <body>
- <header>
- <div id="formId"></div>
- <div class="wrapper">
- <div class="cart">
- <a href="#/cart">
- <span id="cartQuantity"></span>
- <img src="xz.png" />
- </a>
- </div>
- <div id="ownCab"></div>
- </div>
- </header>
- <div id='content'>
- <aside id='aside'>
- Категории
- </aside>
- <main id='main'>
- Контент
- </main>
- </div>
- <script>
- //----------------------Store---------------------------------------
- function createStore(reducer){
- let state = reducer(undefined, {})
- let cbs = []
- const getState = () => state
- const subscribe = cb => (cbs.push(cb),
- () => cbs = cbs.filter(c => c !== cb))
- const dispatch = action => {
- if (typeof action === 'function'){
- return action(dispatch, getState)
- }
- const newState = reducer(state, action)
- if (newState !== state){
- state = newState
- for (let cb of cbs) cb()
- }
- }
- return {
- getState,
- dispatch,
- subscribe
- }
- }
- //-----------------------------------------------------------------------------
- //написать jwtDecode = token =>({})
- const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2MWE0ZTA1MmM3NTBjMTJiYTZiYTQwMjkiLCJsb2dpbiI6InZsYWRCcmF1bjQiLCJhY2wiOlsiNjFhNGUwNTJjNzUwYzEyYmE2YmE0MDI5IiwidXNlciJdfSwiaWF0IjoxNjM4NTM5NTUzfQ.oPRus9nGS1rg69eKu8rK-tMi4V-hN5HXE0NOzAc5K4k";
- //выкусить из токена серединку
- //сделать base64 декод (atob)
- //с результатом сделать JSON.parse
- const jwtDecode = token =>{
- try{
- let mid=token.split('.');
- let tok=mid[1];
- let tokenDecode=atob(tok);
- let finalTok=JSON.parse(tokenDecode);
- return finalTok;
- }catch(e){
- console.log(e);
- }
- }
- console.log(jwtDecode(token));
- //-------------------------------------------------------------------------------
- function authReducer(state, {type, token}) {
- if(!state) {
- if (localStorage.authToken) {
- type = 'AUTH_LOGIN'
- token = localStorage.authToken
- } else {
- return {}
- }
- }
- if (type === 'AUTH_LOGIN') {
- localStorage.authToken = token
- let payload = jwtDecode(token)
- if (typeof payload !== 'object') {
- return {}
- }
- return {token, payload}
- }
- if (type === 'AUTH_LOGOUT') {
- // debugger
- localStorage.removeItem('authToken')
- return {}
- }
- return state
- }
- // const store = createStore(authReducer)
- const actionAuthLogin = token => ({type: 'AUTH_LOGIN', token})
- const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'})
- // login.onclick = () => store.dispatch(actionAuthLogin(token))
- // logout.onclick = () => store.dispatch(actionAuthLogout())
- //------------------------------------------------------------------------------
- function combineReducers(reducers) {
- return (state = {}, action) => {
- const newState = {}
- for (const [reducerName, reducer] of Object.entries(reducers)) {
- let newSubstate = reducer(state[reducerName], action)
- if (newSubstate !== state[reducerName]) {
- newState[reducerName] = newSubstate
- }
- }
- if (Object.entries(newState).length !== 0) {
- return {...state, ...newState}
- } else return state
- }
- }
- //-------------------------------------------------------------------------------
- function promiseReducer(state = {}, { type, name, status, payload, error }) {
- //{
- // login: {status, payload, error}
- // catById: {status, payload, error}
- //}
- if (type === 'PROMISE') {
- return {
- ...state,
- [name]: { status, payload, error }
- }
- }
- return state
- }
- 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)) // 1. {delay1000: {status: 'PENDING'}}
- try {
- let payload = await promise
- dispatch(actionResolved(name, payload))
- return payload
- }
- catch (error) {
- dispatch(actionRejected(name, error))
- }
- }
- // const store = createStore(promiseReducer)
- // store.subscribe(() => console.log(store.getState()))
- // const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
- // store.dispatch(actionPromise('delay1000', delay(1000)))
- // store.dispatch(actionPromise('delay2000', delay(2000)))
- // store.dispatch(actionPromise('failedfetch', fetch('https://swapi.dev/api/people/1/')
- // .then(res => res.json())))
- //--------------------------------------------------------------
- function cartReducer(state = {}, {type, good = {}, count = 1}) {
- const {_id} = good
-
- if(!count) {
- return state
- }
- const types = {
- CART_ADD(){
- //берет старую позицию, и добавляет count к текущему количеству.
- //если позиции нет - то добавляет к 0 (т. е. в первый раз будет count)
- count = +count
- return {
- ...state,
- [_id]: {good, count:(state[_id] ? state[_id].count : 0) + count}
- }
- },
- CART_CHANGE(){
- //тупо меняет позицию
- return {
- ...state,
- [_id]: {good, count}
- }
- },
- CART_REMOVE(){
- //надо как-то создать объект без ключа
- let {[_id]: remove, ...rest} = state
- return rest
- },
- CART_CLEAR(){
- //самое простое
- return {}
- }
- }
- if(type in types) {
- return types[type]()
- }
- return state
- }
- 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 combinedReducer = combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer}) //тут еще
- const store = createStore(combinedReducer)
- console.log(store.getState())
- // store.dispatch(actionPromise('delay1000', delay('1000')))
- // store.dispatch(actionAuthLogin(token))
- //--------------------------------------------------------------------------------
- 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.errors && !data.data)
- throw new Error(JSON.stringify(data.errors))
- return data.data[Object.keys(data.data)[0]]
- })
- const backURL = 'http://shop-roles.asmer.fs.a-level.com.ua'
- const gql = getGQL(backURL + '/graphql')
- //-------------------------Actions----------------------------------------
- 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 description images {
- url
- }
- }
- subCategories {
- name _id goods {
- _id name description
- }
- }
- }
- }`, { q: JSON.stringify([{ _id }]) }))
- const actionGoodById = (_id) =>
- actionPromise('goodById', gql(`query goodById($q: String){
- GoodFindOne(query: $q){
- _id name description price images{
- url
- }
- }
- }`, { q: JSON.stringify([{ _id }]) }))
- store.dispatch(actionRootCats())
- const actionOrder = () =>
- async (dispatch, getState) => {
- let {cart} = store.getState()
- const orderGoods = Object.entries(cart)
- .map(([_id, {good, count}]) => ({good: {_id}, count}))
- let result = await dispatch(actionPromise('order', gql(`
- mutation newOrder($order:OrderInput){
- OrderUpsert(order:$order)
- { _id total }
- }`, {order: {orderGoods}})))
- if(result?._id) {
- store.dispatch(actionCartClear())
- }
- }
- const actionMyOrders = () =>
- actionPromise('orderfind', gql(`query orderfind{
- OrderFind(query: "[{}]"){
- _id createdAt total orderGoods{
- _id createdAt price count
- good{
- _id description name images{
- _id url
- }
- }
- }
- }
- }`))
- 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))
- }
- }
- const actionRegister = (login, password) =>
- actionPromise('register', gql(`mutation registration($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 log = await dispatch(actionRegister(login, password))
- if (log) {
- let token = await dispatch(actionLogin(login, password))
- if (token){
- dispatch(actionAuthLogin(token))
- }
- }
- }
- //--------------------------main Page--------------------------------------------
- store.subscribe(() => {
- const { promise } = store.getState()
- console.log('------------')
- console.log(promise)
- if (promise?.rootCats?.payload) {
- aside.innerHTML = ''
- for (const { _id, name } of promise?.rootCats?.payload) {
- const link = document.createElement('a')
- link.href = `#/category/${_id}`
- link.innerText = name
- aside.append(link)
- }
- }
- })
- store.subscribe(() => {
- const { promise } = store.getState()
- const [, route, _id] = location.hash.split('/')
- if (promise?.catById?.payload && route === 'category') {
- const { name } = promise.catById.payload
- main.innerHTML = `<h1 class="Jorik">${name}</h1>`
- if (promise.catById.payload?.subCategories) {
- for (let { _id, name } of promise.catById.payload.subCategories) {
- const podCat = document.createElement('a')
- podCat.href = `#/category/${_id}`
- podCat.innerText = name
- main.append(podCat)
- }
- }
- for (const good of promise.catById.payload.goods) {
- const {_id, name, price, images} = good
- const card = document.createElement('div')
- card.innerHTML = `<h2>${name}</h2>
- <img src="${backURL}/${images[0].url}"/>
- <div>
- <b>Стоимость:</b> <b><sub>${price}UAH</sub></b>
- <br><a href=#/good/${_id}>Страница товара</a>
- </div>`
- main.append(card)
- let btn = document.createElement('button')
- btn.innerText = 'Добавить в корзину'
- btn.style.marginLeft=50+'%'
- btn.onclick = () => store.dispatch(actionCartAdd(good, 1))
- card.append(btn)
- }
- }
- })
- //-----------------------Korzina---------------------------
- store.subscribe(() => {
- const {cart} = store.getState()
- console.log(cart)
- if (cart){
- let num = 0
- cartQuantity.innerText = ''
- for (let key in cart) {
- num+=cart[key].count
- }
- cartQuantity.innerText = num
- }
- })
- function showCart() {
- const { cart } = store.getState()
- console.log(cart)
- main.innerHTML = ``
- let num = 1
- let count = 0
- let total = 0
- let h = document.createElement('h2')
- h.innerText = 'Корзина';
- h.style.borderBottom = 1+'px solid black'
- main.append(h)
- const table = document.createElement('table')
- for (let key in cart) {
- let { good, count } = cart[key]
- console.log(good, count)
- let cartKey = document.createElement('tr')
- cartKey.innerHTML = `<td>${num++}</td><td><img src="${backURL}/${good.images[0].url}"/></td><td>${good.name}</td>
- <td>${good.price} UAH</td>`
- let addBtnTd = document.createElement('td')
- let addBtn = document.createElement('button')
- addBtn.innerText = '+'
- addBtnTd.append(addBtn)
- let chngTd = document.createElement('td')
- let chng = document.createElement('input')
- chng.type = 'number'
- chng.min = 0
- chng.max = 50
- chng.value = count
- chngTd.append(chng)
- let subBtnTd = document.createElement('td')
- let subBtn = document.createElement('button')
- subBtn.innerText = '-'
- subBtnTd.append(subBtn)
- let deleteBtnTd = document.createElement('td')
- let deleteBtn = document.createElement('button')
- deleteBtn.innerText = 'Удалить'
- deleteBtnTd.append(deleteBtn)
- chng.oninput = () => store.dispatch(actionCartChange(good, chng.value))
- deleteBtn.onclick = () => {
- cartKey.remove()
- store.dispatch(actionCartRemove(good))
- }
- addBtn.onclick = () => {
- store.dispatch(actionCartAdd(good, 1))
- chng.value++
- }
- subBtn.onclick = () => {
- store.dispatch(actionCartAdd(good, -1))
- chng.value--
- }
- cartKey.append(addBtnTd, chngTd, subBtnTd, deleteBtnTd)
- table.append(cartKey)
- main.append(table)
- count += parseInt(chng.value)
- total += good.price * chng.value
- }
- let clearBtn = document.createElement('button')
- clearBtn.innerText = "Очистить корзину";
- Object.entries(cart).length > 0 ? main.append(clearBtn) : null
- clearBtn.onclick = () => {
- store.dispatch(actionCartClear())
- table.remove()
- clearBtn.style.display = 'none';
- canOrder.style.display = 'none';
- }
- let canOrder = document.createElement('button')
- canOrder.innerText = "Оформить заказ"
- Object.entries(cart).length > 0 ? main.append(canOrder) : null
- if (localStorage.authToken) {
- canOrder.disabled = false
- } else {
- canOrder.disabled = true
- }
- canOrder.onclick = () => {
- store.dispatch(actionOrder())
- table.remove()
- clearBtn.style.display = 'none';
- canOrder.style.display = 'none';
- }
- }
- //-----------------opis tovara--------------------------
- store.subscribe(() => {
- const { promise } = store.getState()
- const [, route, _id] = location.hash.split('/')
- if (promise?.goodById?.payload && route === 'good' && location.href.includes(`#/good/${_id}`)) {
- main.innerHTML = ``
- let { _id, name, price, images, description } = promise.goodById.payload
- let goodItem = document.createElement('div')
- goodItem.className = 'good_item'
- goodItem.innerHTML = `<h2>${name}</h2>
- <img src="${backURL}/${images[0].url}"/>
- <div>
- <p><b>Стоимость:</b> <b><sub>${price} UAH</sub></b></strong></p>
- <p><b>Описание:</b> <sub>${description}</sub></p>
- </div>`
- main.append(goodItem)
- }
- })
- //--------------------------Orders-----------------------------
- store.subscribe(() => {
- const {promise} = store.getState()
- const [,route, _id] = location.hash.split('/')
- if (promise?.orderfind?.payload && route === 'dashboard'){
- main.innerHTML = ''
- let title = document.createElement('h2')
- title.innerText = 'Ваши заказы';
- title.style.textAlign='left'
- title.style.borderBottom=1+'px solid black'
- main.append(title)
- for (let order of promise.orderfind.payload) {
- let quantity = 0
- const {total, orderGoods} = order
- let table = document.createElement('table')
- let thead = document.createElement('thead')
- thead.innerHTML = `<th class="user-order">Дата заказа</th>
- <th>Наименование</th>
- <th>Количество</th>
- <th>Цена</th>`
- table.append(thead)
- for (let {createdAt, count, good, price} of orderGoods){
- quantity += count;
- let date = new Date(+createdAt).toLocaleDateString();
- let tr = document.createElement('tr')
- tr.innerHTML = `<td>${date}</td>
- <td><figure class='good-img'>
- <img src="${backURL}/${good.images[0].url}">
- <figcaption>${good.name}</figcaption>
- </figure></td>
- <td>${count}</td>
- <td>${price}</td>`
- table.append(tr)
- }
- let totalOrderAmount = document.createElement('tr')
- totalOrderAmount.innerHTML = `<th class="user-order">Всего товаров в заказе: ${quantity}</th>
- <th>Общая сумма заказа: ${total}</th>`
- table.append(totalOrderAmount)
- main.append(table)
- }
- }
- else if (!promise?.orderfind?.payload && route === 'dashboard'){
- main.innerHTML = ''
- let title = document.createElement('h2')
- title.textContent = 'Вы еще не сделали заказ';
- main.append(title)
- }
- })
- //----------------------Login-------------------------------
- store.subscribe(() => {
- const {auth} = store.getState()
- const {payload} = auth
- if (payload?.sub ) {
- ownCab.innerHTML = ''
- //ownCab.style.marginTop=10+'px'
- //ownCab.style.border=5+'px solid blue'
- //ownCab.style.width=17+'%'
- const {id, login} = payload.sub
- const userName = document.createElement('div')
- userName.innerHTML = `Hello, ${login}`
- const userOrders = document.createElement('a')
- //userOrders.style.marginRight=10+'px'
- userOrders.innerText = 'Your Orders'
- userOrders.href = `#/dashboard/`
- let logout = document.createElement('button')
- logout.textContent = 'Exit'
- logout.onclick = () => {
- formId.innerHTML = ''
- store.dispatch(actionAuthLogout());
- }
- ownCab.append(userName, userOrders, logout)
- } else {
- ownCab.innerHTML = ''
- }
- })
- store.subscribe(() => {
- const {auth} = store.getState()
- if (!auth?.payload){
- formId.innerHTML = '';
- let twoBtns=document.createElement('span')
- let loginBtn = document.createElement('a');
- let regBtn = document.createElement('a');
- twoBtns.append(loginBtn, regBtn)
- twoBtns.style.border=1+'px dotted green'
- formId.append(twoBtns)
- loginBtn.innerText = 'LogIn'
- regBtn.innerText = 'Registration'
- loginBtn.onclick = () => {
- loginBtn.style.display = 'none'
- regBtn.style.display= 'none'
- let form = document.createElement('form')
- let login = document.createElement('input')
- login.type = 'text'
- login.placeholder = 'Login'
- let password = document.createElement('input')
- password.placeholder = 'Password'
- password.type = 'password'
- let btn = document.createElement('a')
- btn.innerText = 'Enter'
- btn.onclick = () => {
- if (login.value !== '' && password.value !== '') {
- btn.href = `#/login/${login.value}*${password.value}`
- }
- formId.innerHTML = ''
- }
- form.append(login, password, btn)
- formId.append(form)
- }
- regBtn.onclick = () => {
- loginBtn.style.display = 'none'
- regBtn.style.display= 'none'
- let form = document.createElement('form')
- let login = document.createElement('input')
- login.type = 'text'
- login.placeholder = 'Login'
- let pass = document.createElement('input')
- pass.placeholder = 'Password'
- pass.type = 'password'
- let btn = document.createElement('a')
- btn.innerText = 'Registration'
- btn.onclick = () => {
- if (login.value !== '' && pass.value !== '') {
- btn.href = `#/register/${login.value}*${pass.value}`
- }
- formId.innerHTML = ''
- }
- form.append(login, password, button)
- formId.append(form)
- }
- }
- })
- //-------------------------------------------------------------
- window.onhashchange = () => {
- const [, route, _id] = location.hash.split('/')
- const routes = {
- category() {
- store.dispatch(actionCatById(_id))
- },
- good() {
- //задиспатчить actionGoodById
- store.dispatch(actionGoodById(_id))
- console.log('ТОВАРОСТРАНИЦА')
- },
- login(){
- let data = _id.split('*')
- store.dispatch(actionFullLogin(data[0], data[1]))
- console.log('ЛОГИН')
- },
- register(){
- let data = _id.split('*')
- store.dispatch(actionFullRegister(data[0], data[1]))
- console.log('РЕГА')
- },
- cart(){
- showCart()
- console.log('Сделать страницу с позициями, полями ввода колличества, картинками и кнопкой')
- },
- dashboard(){
- store.dispatch(actionMyOrders())
- console.log('Прочитать бывшие заказы')
- }
- }
- if (route in routes)
- routes[route]()
- }
- window.onhashchange()
- </script>
- </body>
- </html>
|