refactor: update ProductCard, ProductGrid, and MarketPage components for improved product handling and internal state management
- Removed direct store dependency from ProductCard.vue, replacing it with event emission for adding products to the cart. - Enhanced ProductGrid.vue to manage product detail dialog internally, improving user interaction and state handling. - Streamlined MarketPage.vue by removing redundant product detail dialog logic, focusing on cleaner component structure. - Updated event handling for adding products to the cart with quantity support, enhancing flexibility in product management. These changes improve the modularity and maintainability of the market components, providing a better user experience when interacting with products.
This commit is contained in:
parent
3f47d2ff26
commit
56bcb8ec04
4 changed files with 53 additions and 68 deletions
|
|
@ -101,7 +101,6 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useMarketStore } from '@/modules/market/stores/market'
|
|
||||||
import { Card, CardContent, CardFooter, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardFooter, CardTitle } from '@/components/ui/card'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
@ -114,16 +113,16 @@ interface Props {
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
// const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
// 'view-details': [product: Product]
|
'add-to-cart': [product: Product]
|
||||||
// 'view-stall': [stallId: string]
|
'view-details': [product: Product]
|
||||||
// }>()
|
'view-stall': [stallId: string]
|
||||||
|
}>()
|
||||||
|
|
||||||
const marketStore = useMarketStore()
|
|
||||||
const imageError = ref(false)
|
const imageError = ref(false)
|
||||||
|
|
||||||
const addToCart = () => {
|
const addToCart = () => {
|
||||||
marketStore.addToStallCart(props.product, 1)
|
emit('add-to-cart', props.product)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleImageError = () => {
|
const handleImageError = () => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="product-grid-container">
|
||||||
<!-- Loading State -->
|
<!-- Loading State -->
|
||||||
<div v-if="isLoading" class="flex justify-center items-center min-h-64">
|
<div v-if="isLoading" class="flex justify-center items-center min-h-64">
|
||||||
<div class="flex flex-col items-center space-y-4">
|
<div class="flex flex-col items-center space-y-4">
|
||||||
|
|
@ -23,18 +23,28 @@
|
||||||
v-for="product in products"
|
v-for="product in products"
|
||||||
:key="product.id"
|
:key="product.id"
|
||||||
:product="product as Product"
|
:product="product as Product"
|
||||||
@add-to-cart="$emit('add-to-cart', $event)"
|
@add-to-cart="handleAddToCart"
|
||||||
@view-details="$emit('view-details', $event)"
|
@view-details="handleViewDetails"
|
||||||
@view-stall="$emit('view-stall', $event)"
|
@view-stall="$emit('view-stall', $event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Product Detail Dialog - Now managed internally -->
|
||||||
|
<ProductDetailDialog
|
||||||
|
v-if="selectedProduct"
|
||||||
|
:product="selectedProduct"
|
||||||
|
:isOpen="showProductDetail"
|
||||||
|
@close="closeProductDetail"
|
||||||
|
@add-to-cart="handleDialogAddToCart"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { Package as EmptyIcon } from 'lucide-vue-next'
|
import { Package as EmptyIcon } from 'lucide-vue-next'
|
||||||
import ProductCard from './ProductCard.vue'
|
import ProductCard from './ProductCard.vue'
|
||||||
|
import ProductDetailDialog from './ProductDetailDialog.vue'
|
||||||
import type { Product } from '../types/market'
|
import type { Product } from '../types/market'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -66,9 +76,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
defineEmits<{
|
const emit = defineEmits<{
|
||||||
'add-to-cart': [product: Product]
|
'add-to-cart': [product: Product, quantity?: number]
|
||||||
'view-details': [product: Product]
|
|
||||||
'view-stall': [stallId: string]
|
'view-stall': [stallId: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
|
@ -89,4 +98,30 @@ const gridClasses = computed(() => {
|
||||||
|
|
||||||
return classes.join(' ')
|
return classes.join(' ')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Internal state for product detail dialog
|
||||||
|
const showProductDetail = ref(false)
|
||||||
|
const selectedProduct = ref<Product | null>(null)
|
||||||
|
|
||||||
|
// Handle view details internally
|
||||||
|
const handleViewDetails = (product: Product) => {
|
||||||
|
selectedProduct.value = product
|
||||||
|
showProductDetail.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeProductDetail = () => {
|
||||||
|
showProductDetail.value = false
|
||||||
|
selectedProduct.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle add to cart from product card (quick add, quantity 1)
|
||||||
|
const handleAddToCart = (product: Product) => {
|
||||||
|
emit('add-to-cart', product, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle add to cart from dialog (with custom quantity)
|
||||||
|
const handleDialogAddToCart = (product: Product, quantity: number) => {
|
||||||
|
emit('add-to-cart', product, quantity)
|
||||||
|
closeProductDetail()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -78,7 +78,6 @@
|
||||||
empty-title="No products found"
|
empty-title="No products found"
|
||||||
empty-message="Try adjusting your search or filters"
|
empty-message="Try adjusting your search or filters"
|
||||||
@add-to-cart="addToCart"
|
@add-to-cart="addToCart"
|
||||||
@view-details="viewProduct"
|
|
||||||
@view-stall="viewStall"
|
@view-stall="viewStall"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -91,14 +90,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Product Detail Dialog -->
|
|
||||||
<ProductDetailDialog
|
|
||||||
v-if="selectedProduct"
|
|
||||||
:product="selectedProduct"
|
|
||||||
:isOpen="showProductDetail"
|
|
||||||
@close="closeProductDetail"
|
|
||||||
@add-to-cart="handleAddToCart"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -116,7 +107,6 @@ import { ShoppingCart } from 'lucide-vue-next'
|
||||||
import MarketFuzzySearch from '../components/MarketFuzzySearch.vue'
|
import MarketFuzzySearch from '../components/MarketFuzzySearch.vue'
|
||||||
import ProductGrid from '../components/ProductGrid.vue'
|
import ProductGrid from '../components/ProductGrid.vue'
|
||||||
import CategoryFilterBar from '../components/CategoryFilterBar.vue'
|
import CategoryFilterBar from '../components/CategoryFilterBar.vue'
|
||||||
import ProductDetailDialog from '../components/ProductDetailDialog.vue'
|
|
||||||
import type { Product } from '../types/market'
|
import type { Product } from '../types/market'
|
||||||
import type { FuzzySearchOptions } from '@/composables/useFuzzySearch'
|
import type { FuzzySearchOptions } from '@/composables/useFuzzySearch'
|
||||||
|
|
||||||
|
|
@ -149,9 +139,6 @@ let unsubscribe: (() => void) | null = null
|
||||||
// Fuzzy search state
|
// Fuzzy search state
|
||||||
const searchResults = ref<Product[]>([])
|
const searchResults = ref<Product[]>([])
|
||||||
|
|
||||||
// Product detail dialog state
|
|
||||||
const showProductDetail = ref(false)
|
|
||||||
const selectedProduct = ref<Product | null>(null)
|
|
||||||
|
|
||||||
// Fuzzy search configuration for products and stalls
|
// Fuzzy search configuration for products and stalls
|
||||||
const searchOptions: FuzzySearchOptions<Product> = {
|
const searchOptions: FuzzySearchOptions<Product> = {
|
||||||
|
|
@ -252,24 +239,10 @@ const retryLoadMarket = () => {
|
||||||
loadMarket()
|
loadMarket()
|
||||||
}
|
}
|
||||||
|
|
||||||
const addToCart = (product: any) => {
|
const addToCart = (product: Product, quantity?: number) => {
|
||||||
marketStore.addToCart(product)
|
marketStore.addToStallCart(product, quantity || 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewProduct = (product: Product) => {
|
|
||||||
selectedProduct.value = product
|
|
||||||
showProductDetail.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeProductDetail = () => {
|
|
||||||
showProductDetail.value = false
|
|
||||||
selectedProduct.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAddToCart = (product: Product, quantity: number) => {
|
|
||||||
marketStore.addToCart({ ...product, quantity })
|
|
||||||
closeProductDetail()
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewStall = (stallId: string) => {
|
const viewStall = (stallId: string) => {
|
||||||
// Navigate to the stall view page
|
// Navigate to the stall view page
|
||||||
|
|
|
||||||
|
|
@ -126,18 +126,10 @@
|
||||||
:empty-message="searchQuery || selectedCategories.length > 0
|
:empty-message="searchQuery || selectedCategories.length > 0
|
||||||
? 'Try adjusting your filters or search terms'
|
? 'Try adjusting your filters or search terms'
|
||||||
: 'This stall doesn\'t have any products yet'"
|
: 'This stall doesn\'t have any products yet'"
|
||||||
@view-details="viewProductDetails"
|
|
||||||
@view-stall="viewStall"
|
@view-stall="viewStall"
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Product Detail Dialog -->
|
|
||||||
<ProductDetailDialog
|
|
||||||
v-if="selectedProduct"
|
|
||||||
:product="selectedProduct"
|
|
||||||
:isOpen="showProductDetail"
|
|
||||||
@close="closeProductDetail"
|
|
||||||
@add-to-cart="handleAddToCart"
|
@add-to-cart="handleAddToCart"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -158,7 +150,6 @@ import {
|
||||||
import { ArrowLeft, Store, X } from 'lucide-vue-next'
|
import { ArrowLeft, Store, X } from 'lucide-vue-next'
|
||||||
import FuzzySearch from '@/components/ui/fuzzy-search/FuzzySearch.vue'
|
import FuzzySearch from '@/components/ui/fuzzy-search/FuzzySearch.vue'
|
||||||
import ProductGrid from '../components/ProductGrid.vue'
|
import ProductGrid from '../components/ProductGrid.vue'
|
||||||
import ProductDetailDialog from '../components/ProductDetailDialog.vue'
|
|
||||||
import type { Product, Stall } from '../types/market'
|
import type { Product, Stall } from '../types/market'
|
||||||
import type { FuzzySearchOptions } from '@/composables/useFuzzySearch'
|
import type { FuzzySearchOptions } from '@/composables/useFuzzySearch'
|
||||||
|
|
||||||
|
|
@ -172,8 +163,6 @@ const searchResults = ref<Product[]>([])
|
||||||
const searchQuery = ref('')
|
const searchQuery = ref('')
|
||||||
const sortBy = ref('name')
|
const sortBy = ref('name')
|
||||||
const selectedCategories = ref<string[]>([])
|
const selectedCategories = ref<string[]>([])
|
||||||
const showProductDetail = ref(false)
|
|
||||||
const selectedProduct = ref<Product | null>(null)
|
|
||||||
const logoError = ref(false)
|
const logoError = ref(false)
|
||||||
|
|
||||||
// Fuzzy search configuration for stall products
|
// Fuzzy search configuration for stall products
|
||||||
|
|
@ -280,20 +269,10 @@ const handleSearchResults = (results: Product[]) => {
|
||||||
// For now, we'll track it separately
|
// For now, we'll track it separately
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewProductDetails = (product: Product) => {
|
const handleAddToCart = (product: Product, quantity?: number) => {
|
||||||
selectedProduct.value = product
|
marketStore.addToStallCart(product, quantity || 1)
|
||||||
showProductDetail.value = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeProductDetail = () => {
|
|
||||||
showProductDetail.value = false
|
|
||||||
selectedProduct.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAddToCart = (product: Product, quantity: number) => {
|
|
||||||
marketStore.addToStallCart(product, quantity)
|
|
||||||
closeProductDetail()
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewStall = (otherStallId: string) => {
|
const viewStall = (otherStallId: string) => {
|
||||||
if (otherStallId !== stallId.value) {
|
if (otherStallId !== stallId.value) {
|
||||||
|
|
@ -322,7 +301,6 @@ watch(() => route.params.stallId, (newStallId) => {
|
||||||
if (newStallId && newStallId !== stallId.value) {
|
if (newStallId && newStallId !== stallId.value) {
|
||||||
// Reset filters when navigating to a different stall
|
// Reset filters when navigating to a different stall
|
||||||
clearFilters()
|
clearFilters()
|
||||||
closeProductDetail()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue