feat: add adding items to cart
This commit is contained in:
parent
f67d2d187e
commit
90677c7ee8
@ -12,7 +12,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"vue": "^3.5.13"
|
||||
"vue": "^3.5.13",
|
||||
"vue3-toastify": "^0.2.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
|
||||
61
src/App.vue
61
src/App.vue
@ -1,11 +1,12 @@
|
||||
<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 CardList from '@/components/CardList.vue'
|
||||
import Drawer from '@/components/Drawer.vue'
|
||||
import axios from 'axios'
|
||||
import Spinner from '@/components/Spinner.vue'
|
||||
|
||||
import { toast } from 'vue3-toastify'
|
||||
import 'vue3-toastify/dist/index.css';
|
||||
const showDrawer = ref(false);
|
||||
const items = ref([]);
|
||||
const isLoading = ref(false);
|
||||
@ -37,25 +38,68 @@ import { onMounted, provide, reactive, ref, watch } from 'vue'
|
||||
onMounted(fetchItems)
|
||||
watch(filters, fetchItems)
|
||||
|
||||
const addItemToFavorite = async (item) => {
|
||||
const toggleToFavorite = async (item) => {
|
||||
try {
|
||||
item.isFavorite = !item.isFavorite;
|
||||
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) {
|
||||
console.log('Error', e)
|
||||
}
|
||||
}
|
||||
|
||||
provide('openDrawer', () => showDrawer.value = true)
|
||||
provide('closeDrawer', () => showDrawer.value = false)
|
||||
const cart = computed(() => items.value.filter(i => i.isAdded));
|
||||
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>
|
||||
<template>
|
||||
<Drawer v-if="showDrawer" />
|
||||
<Drawer v-if="showDrawer" :cart="cart" />
|
||||
|
||||
<div
|
||||
class="mt-14 w-4/5 mx-auto bg-white rounded-xl shadow-xl"
|
||||
>
|
||||
<Header/>
|
||||
<Header />
|
||||
|
||||
<div class="p-10">
|
||||
<div class="flex justify-between items-center mb-8">
|
||||
@ -85,7 +129,8 @@ import { onMounted, provide, reactive, ref, watch } from 'vue'
|
||||
<CardList
|
||||
v-if="items.length > 0"
|
||||
:items="items"
|
||||
@addToFavorite="addItemToFavorite"
|
||||
@toggle-to-favorite="toggleToFavorite"
|
||||
@toggle-to-cart="toggleToCart"
|
||||
/>
|
||||
<h2 v-else>Пусто</h2>
|
||||
</div>
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
<script setup>
|
||||
import Card from './Card.vue'
|
||||
|
||||
const onClickAdd = () => {
|
||||
alert('Добавлено в корзину')
|
||||
}
|
||||
|
||||
defineProps({
|
||||
items: Array
|
||||
})
|
||||
|
||||
defineEmits([
|
||||
'addToFavorite',
|
||||
'toggleToFavorite',
|
||||
'toggleToCart',
|
||||
])
|
||||
</script>
|
||||
|
||||
@ -25,8 +22,8 @@
|
||||
:price="item.price"
|
||||
:is-added="item.isAdded"
|
||||
:is-favorite="item.isFavorite"
|
||||
:on-click-add="onClickAdd"
|
||||
:on-click-favorite="() => $emit('addToFavorite', item)"
|
||||
:on-click-add="() => $emit('toggleToCart', item)"
|
||||
:on-click-favorite="() => $emit('toggleToFavorite', item)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,13 +1,25 @@
|
||||
<script setup>
|
||||
import { decimalFormatter } from '@/utils/helpers.js'
|
||||
|
||||
defineProps({
|
||||
item: Object,
|
||||
})
|
||||
|
||||
defineEmits([
|
||||
'onRemove'
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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">
|
||||
<p>Мужские кроссовки Nike Blazer Mid Suede</p>
|
||||
<p>{{item.title}}</p>
|
||||
|
||||
<div class="flex justify-between mt-2">
|
||||
<b>12 990 руб.</b>
|
||||
<img src="/close.svg" alt="Close" class="cursor-pointer transition opacity-40 hover:opacity-100">
|
||||
<b>{{ decimalFormatter.format(item.price) }} руб.</b>
|
||||
<img @click="() => $emit('onRemove', item)" src="/close.svg" alt="Close" class="cursor-pointer transition opacity-40 hover:opacity-100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import CartItem from './CartItem.vue'
|
||||
import {inject} from 'vue';
|
||||
|
||||
defineProps({
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const { toggleToCart } = inject('cart')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-4">
|
||||
<CartItem/>
|
||||
<CartItem/>
|
||||
<CartItem/>
|
||||
<CartItem/>
|
||||
<CartItem v-for="item in items" :key="item.id" :item="item" @on-remove="toggleToCart" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -2,8 +2,16 @@
|
||||
import DrawerHead from './DrawerHead.vue'
|
||||
import CartItemList from '@/components/CartItemList.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>
|
||||
|
||||
<template>
|
||||
@ -13,19 +21,26 @@
|
||||
<div @click.stop class="bg-white w-96 h-full fixed top-0 right-0 z-20 p-8">
|
||||
<DrawerHead/>
|
||||
|
||||
<CartItemList />
|
||||
<div v-if="cart.length > 0">
|
||||
<CartItemList :items="cart" />
|
||||
|
||||
<div class="flex flex-col gap-2 my-6">
|
||||
<div class="flex justify-between gap-2">
|
||||
<span>Итого:</span>
|
||||
<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>900 руб.</b>
|
||||
<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">
|
||||
@ -33,4 +48,10 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 v-else class="text-lg font-bold text-center mt-10 text-slate-500">
|
||||
В корзине нет товаров
|
||||
</h2>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { inject } from 'vue'
|
||||
|
||||
const closeDrawer = inject('closeDrawer')
|
||||
const { closeDrawer } = inject('cart')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@ -1,46 +1,43 @@
|
||||
<script setup>
|
||||
import { inject } from 'vue'
|
||||
import { decimalFormatter } from '@/utils/helpers.js'
|
||||
|
||||
const openDrawer = inject('openDrawer')
|
||||
const { openDrawer } = inject('cart')
|
||||
|
||||
const {totalPrice} = inject('cart')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header
|
||||
class="flex justify-between border-b border-slate-100 px-11 py-10"
|
||||
>
|
||||
<header class="flex justify-between border-b border-slate-100 px-11 py-10">
|
||||
<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>
|
||||
<h2 class="text-xl font-bold uppercase">
|
||||
Vue Sneakers
|
||||
</h2>
|
||||
<p class="text-slate-500">
|
||||
Магазин лучших кроссовок
|
||||
</p>
|
||||
<h2 class="text-xl font-bold uppercase">Vue Sneakers</h2>
|
||||
<p class="text-slate-500">Магазин лучших кроссовок</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="flex items-center gap-8">
|
||||
<li @click="openDrawer" class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer">
|
||||
<img src="/cart.svg" alt="Cart">
|
||||
<b>
|
||||
1 205 руб.
|
||||
</b>
|
||||
<li
|
||||
@click="openDrawer"
|
||||
class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer"
|
||||
>
|
||||
<img src="/cart.svg" alt="Cart" />
|
||||
<b> {{ decimalFormatter.format(totalPrice)}} руб. </b>
|
||||
</li>
|
||||
|
||||
<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>
|
||||
</li>
|
||||
|
||||
<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>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</header>
|
||||
</template>
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import { createApp } from 'vue'
|
||||
import './assets/main.css'
|
||||
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-plugin-vue-devtools: "npm:^7.6.8"
|
||||
vue: "npm:^3.5.13"
|
||||
vue3-toastify: "npm:^0.2.8"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
@ -4110,6 +4111,18 @@ __metadata:
|
||||
languageName: node
|
||||
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":
|
||||
version: 3.5.13
|
||||
resolution: "vue@npm:3.5.13"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user