feat: add adding items to cart
This commit is contained in:
parent
f67d2d187e
commit
90677c7ee8
@ -12,7 +12,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"vue": "^3.5.13"
|
"vue": "^3.5.13",
|
||||||
|
"vue3-toastify": "^0.2.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
|||||||
61
src/App.vue
61
src/App.vue
@ -1,11 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, provide, reactive, ref, watch } from 'vue'
|
import { computed, onMounted, provide, reactive, ref, watch } from 'vue'
|
||||||
import Header from '@/components/Header.vue'
|
import Header from '@/components/Header.vue'
|
||||||
import CardList from '@/components/CardList.vue'
|
import CardList from '@/components/CardList.vue'
|
||||||
import Drawer from '@/components/Drawer.vue'
|
import Drawer from '@/components/Drawer.vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import Spinner from '@/components/Spinner.vue'
|
import Spinner from '@/components/Spinner.vue'
|
||||||
|
import { toast } from 'vue3-toastify'
|
||||||
|
import 'vue3-toastify/dist/index.css';
|
||||||
const showDrawer = ref(false);
|
const showDrawer = ref(false);
|
||||||
const items = ref([]);
|
const items = ref([]);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
@ -37,25 +38,68 @@ import { onMounted, provide, reactive, ref, watch } from 'vue'
|
|||||||
onMounted(fetchItems)
|
onMounted(fetchItems)
|
||||||
watch(filters, fetchItems)
|
watch(filters, fetchItems)
|
||||||
|
|
||||||
const addItemToFavorite = async (item) => {
|
const toggleToFavorite = async (item) => {
|
||||||
try {
|
try {
|
||||||
item.isFavorite = !item.isFavorite;
|
item.isFavorite = !item.isFavorite;
|
||||||
await axios.patch(`${import.meta.env.VITE_API_URL}/items/${item.id}`, item);
|
await axios.patch(`${import.meta.env.VITE_API_URL}/items/${item.id}`, item);
|
||||||
|
|
||||||
|
if (item.isFavorite) {
|
||||||
|
toast("Добавлено в избранное", {
|
||||||
|
"type": "success",
|
||||||
|
"hideProgressBar": true,
|
||||||
|
"transition": "slide",
|
||||||
|
"dangerouslyHTMLString": true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error', e)
|
||||||
|
toast("Ошибка при добавлении в избранное", {
|
||||||
|
"type": "error",
|
||||||
|
"hideProgressBar": true,
|
||||||
|
"transition": "slide",
|
||||||
|
"dangerouslyHTMLString": true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleToCart = async (item) => {
|
||||||
|
try {
|
||||||
|
item.isAdded = !item.isAdded;
|
||||||
|
await axios.patch(`${import.meta.env.VITE_API_URL}/items/${item.id}`, item);
|
||||||
|
|
||||||
|
if (item.isAdded) {
|
||||||
|
toast("Добавлено в корзину", {
|
||||||
|
"type": "success",
|
||||||
|
"hideProgressBar": true,
|
||||||
|
"transition": "slide",
|
||||||
|
"dangerouslyHTMLString": true
|
||||||
|
})
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Error', e)
|
console.log('Error', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provide('openDrawer', () => showDrawer.value = true)
|
const cart = computed(() => items.value.filter(i => i.isAdded));
|
||||||
provide('closeDrawer', () => showDrawer.value = false)
|
const totalPrice = computed(() => cart.value.reduce((res, cur) => {
|
||||||
|
return res + cur.price
|
||||||
|
}, 0))
|
||||||
|
|
||||||
|
provide('cart', {
|
||||||
|
cart,
|
||||||
|
toggleToCart,
|
||||||
|
totalPrice,
|
||||||
|
openDrawer: () => showDrawer.value = true,
|
||||||
|
closeDrawer: () => showDrawer.value = false,
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Drawer v-if="showDrawer" />
|
<Drawer v-if="showDrawer" :cart="cart" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="mt-14 w-4/5 mx-auto bg-white rounded-xl shadow-xl"
|
class="mt-14 w-4/5 mx-auto bg-white rounded-xl shadow-xl"
|
||||||
>
|
>
|
||||||
<Header/>
|
<Header />
|
||||||
|
|
||||||
<div class="p-10">
|
<div class="p-10">
|
||||||
<div class="flex justify-between items-center mb-8">
|
<div class="flex justify-between items-center mb-8">
|
||||||
@ -85,7 +129,8 @@ import { onMounted, provide, reactive, ref, watch } from 'vue'
|
|||||||
<CardList
|
<CardList
|
||||||
v-if="items.length > 0"
|
v-if="items.length > 0"
|
||||||
:items="items"
|
:items="items"
|
||||||
@addToFavorite="addItemToFavorite"
|
@toggle-to-favorite="toggleToFavorite"
|
||||||
|
@toggle-to-cart="toggleToCart"
|
||||||
/>
|
/>
|
||||||
<h2 v-else>Пусто</h2>
|
<h2 v-else>Пусто</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Card from './Card.vue'
|
import Card from './Card.vue'
|
||||||
|
|
||||||
const onClickAdd = () => {
|
|
||||||
alert('Добавлено в корзину')
|
|
||||||
}
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
items: Array
|
items: Array
|
||||||
})
|
})
|
||||||
|
|
||||||
defineEmits([
|
defineEmits([
|
||||||
'addToFavorite',
|
'toggleToFavorite',
|
||||||
|
'toggleToCart',
|
||||||
])
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -25,8 +22,8 @@
|
|||||||
:price="item.price"
|
:price="item.price"
|
||||||
:is-added="item.isAdded"
|
:is-added="item.isAdded"
|
||||||
:is-favorite="item.isFavorite"
|
:is-favorite="item.isFavorite"
|
||||||
:on-click-add="onClickAdd"
|
:on-click-add="() => $emit('toggleToCart', item)"
|
||||||
:on-click-favorite="() => $emit('addToFavorite', item)"
|
:on-click-favorite="() => $emit('toggleToFavorite', item)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,13 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import { decimalFormatter } from '@/utils/helpers.js'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
item: Object,
|
||||||
|
})
|
||||||
|
|
||||||
|
defineEmits([
|
||||||
|
'onRemove'
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full border border-slate-100 p-4 rounded-xl flex items-center gap-4">
|
<div class="w-full border border-slate-100 p-4 rounded-xl flex items-center gap-4">
|
||||||
<img class="w-16" src="/sneakers/sneakers-1.jpg" alt="Sneaker">
|
<img class="w-16" :src="item.imageUrl" alt="Sneaker">
|
||||||
|
|
||||||
<div class="flex flex-col justify-between">
|
<div class="flex flex-col justify-between">
|
||||||
<p>Мужские кроссовки Nike Blazer Mid Suede</p>
|
<p>{{item.title}}</p>
|
||||||
|
|
||||||
<div class="flex justify-between mt-2">
|
<div class="flex justify-between mt-2">
|
||||||
<b>12 990 руб.</b>
|
<b>{{ decimalFormatter.format(item.price) }} руб.</b>
|
||||||
<img src="/close.svg" alt="Close" class="cursor-pointer transition opacity-40 hover:opacity-100">
|
<img @click="() => $emit('onRemove', item)" src="/close.svg" alt="Close" class="cursor-pointer transition opacity-40 hover:opacity-100">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,12 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import CartItem from './CartItem.vue'
|
import CartItem from './CartItem.vue'
|
||||||
|
import {inject} from 'vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const { toggleToCart } = inject('cart')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<CartItem/>
|
<CartItem v-for="item in items" :key="item.id" :item="item" @on-remove="toggleToCart" />
|
||||||
<CartItem/>
|
|
||||||
<CartItem/>
|
|
||||||
<CartItem/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -2,8 +2,16 @@
|
|||||||
import DrawerHead from './DrawerHead.vue'
|
import DrawerHead from './DrawerHead.vue'
|
||||||
import CartItemList from '@/components/CartItemList.vue'
|
import CartItemList from '@/components/CartItemList.vue'
|
||||||
import { inject } from 'vue'
|
import { inject } from 'vue'
|
||||||
|
import { decimalFormatter } from '@/utils/helpers.js'
|
||||||
|
|
||||||
const closeDrawer = inject('closeDrawer')
|
const { closeDrawer, totalPrice } = inject('cart')
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
cart: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -13,24 +21,37 @@
|
|||||||
<div @click.stop class="bg-white w-96 h-full fixed top-0 right-0 z-20 p-8">
|
<div @click.stop class="bg-white w-96 h-full fixed top-0 right-0 z-20 p-8">
|
||||||
<DrawerHead/>
|
<DrawerHead/>
|
||||||
|
|
||||||
<CartItemList />
|
<div v-if="cart.length > 0">
|
||||||
|
<CartItemList :items="cart" />
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 my-6">
|
<div class="flex flex-col gap-2 my-6">
|
||||||
<div class="flex justify-between gap-2">
|
<div class="flex justify-between gap-2">
|
||||||
<span>Итого:</span>
|
<span>Итого:</span>
|
||||||
<div class="flex-1 border-b border-dashed"></div>
|
<div class="flex-1 border-b border-dashed"></div>
|
||||||
<b>12 990 руб.</b>
|
<b>{{ decimalFormatter.format(totalPrice) }} руб.</b>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-between gap-2">
|
||||||
|
<span>Налог 5%:</span>
|
||||||
|
<div class="flex-1 border-b border-dashed"></div>
|
||||||
|
<b>{{ decimalFormatter.format(totalPrice * 0.05) }} руб.</b>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-between gap-2">
|
||||||
|
<span>К оплате:</span>
|
||||||
|
<div class="flex-1 border-b border-dashed"></div>
|
||||||
|
<b>{{ decimalFormatter.format(totalPrice * 0.95) }} руб.</b>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="mt-5 bg-lime-500 w-full rounded-xl py-3 text-white transition disabled:bg-slate-300 hover:bg-lime-600 active:bg-lime-700">
|
||||||
|
Оформить заказ
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-between gap-2">
|
|
||||||
<span>Налог 5%:</span>
|
|
||||||
<div class="flex-1 border-b border-dashed"></div>
|
|
||||||
<b>900 руб.</b>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="mt-5 bg-lime-500 w-full rounded-xl py-3 text-white transition disabled:bg-slate-300 hover:bg-lime-600 active:bg-lime-700">
|
|
||||||
Оформить заказ
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2 v-else class="text-lg font-bold text-center mt-10 text-slate-500">
|
||||||
|
В корзине нет товаров
|
||||||
|
</h2>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { inject } from 'vue'
|
import { inject } from 'vue'
|
||||||
|
|
||||||
const closeDrawer = inject('closeDrawer')
|
const { closeDrawer } = inject('cart')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@ -1,46 +1,43 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { inject } from 'vue'
|
import { inject } from 'vue'
|
||||||
|
import { decimalFormatter } from '@/utils/helpers.js'
|
||||||
|
|
||||||
const openDrawer = inject('openDrawer')
|
const { openDrawer } = inject('cart')
|
||||||
|
|
||||||
|
const {totalPrice} = inject('cart')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<header
|
<header class="flex justify-between border-b border-slate-100 px-11 py-10">
|
||||||
class="flex justify-between border-b border-slate-100 px-11 py-10"
|
|
||||||
>
|
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<img src="/logo.png" alt="Logo" class="w-10">
|
<img src="/logo.png" alt="Logo" class="w-10" />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-xl font-bold uppercase">
|
<h2 class="text-xl font-bold uppercase">Vue Sneakers</h2>
|
||||||
Vue Sneakers
|
<p class="text-slate-500">Магазин лучших кроссовок</p>
|
||||||
</h2>
|
|
||||||
<p class="text-slate-500">
|
|
||||||
Магазин лучших кроссовок
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="flex items-center gap-8">
|
<ul class="flex items-center gap-8">
|
||||||
<li @click="openDrawer" class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer">
|
<li
|
||||||
<img src="/cart.svg" alt="Cart">
|
@click="openDrawer"
|
||||||
<b>
|
class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer"
|
||||||
1 205 руб.
|
>
|
||||||
</b>
|
<img src="/cart.svg" alt="Cart" />
|
||||||
|
<b> {{ decimalFormatter.format(totalPrice)}} руб. </b>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer">
|
<li class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer">
|
||||||
<img src="/heart.svg" alt="Heart">
|
<img src="/heart.svg" alt="Heart" />
|
||||||
|
|
||||||
<span>Закладки</span>
|
<span>Закладки</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer">
|
<li class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer">
|
||||||
<img src="/profile.svg" alt="Profile">
|
<img src="/profile.svg" alt="Profile" />
|
||||||
|
|
||||||
<span>Профиль</span>
|
<span>Профиль</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import './assets/main.css'
|
import './assets/main.css'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import Vue3Toastify from 'vue3-toastify';
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
const app = createApp(App);
|
||||||
|
app.use(Vue3Toastify, { autoClose: 1000 });
|
||||||
|
app.mount('#app')
|
||||||
|
|||||||
13
yarn.lock
13
yarn.lock
@ -1290,6 +1290,7 @@ __metadata:
|
|||||||
vite: "npm:^6.0.5"
|
vite: "npm:^6.0.5"
|
||||||
vite-plugin-vue-devtools: "npm:^7.6.8"
|
vite-plugin-vue-devtools: "npm:^7.6.8"
|
||||||
vue: "npm:^3.5.13"
|
vue: "npm:^3.5.13"
|
||||||
|
vue3-toastify: "npm:^0.2.8"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
@ -4110,6 +4111,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"vue3-toastify@npm:^0.2.8":
|
||||||
|
version: 0.2.8
|
||||||
|
resolution: "vue3-toastify@npm:0.2.8"
|
||||||
|
peerDependencies:
|
||||||
|
vue: ">=3.2.0"
|
||||||
|
peerDependenciesMeta:
|
||||||
|
vue:
|
||||||
|
optional: true
|
||||||
|
checksum: 10c0/14d7aadd4663cc83dff25da730e6522d1c6735f6427e7d073b1e574a699a9c54d8be9965842a934ab271a127fb3cf850891092e67760773e119caf94c35f67f5
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"vue@npm:^3.5.13":
|
"vue@npm:^3.5.13":
|
||||||
version: 3.5.13
|
version: 3.5.13
|
||||||
resolution: "vue@npm:3.5.13"
|
resolution: "vue@npm:3.5.13"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user