FIX BUILD ERRORS & AVOID INFINITE RECURSION: Enhance product enrichment and type definitions in MerchantStore component
- Updated the product enrichment logic in MerchantStore.vue to ensure all necessary properties match the Product interface, improving data consistency. - Added optional properties for active status, pending state, and configuration in the Product interface within market.ts, enhancing flexibility for merchant store management. - Improved type assertions in MarketPage.vue and StallView.vue to ensure proper type handling for product data, enhancing type safety and clarity. These changes improve the robustness and reliability of product data handling across the market components, enhancing the overall user experience.
This commit is contained in:
parent
d98dcc58d7
commit
478b83ddd3
4 changed files with 35 additions and 16 deletions
|
|
@ -516,10 +516,25 @@ const loadStallProducts = async () => {
|
||||||
inkey,
|
inkey,
|
||||||
activeStall.value.id!
|
activeStall.value.id!
|
||||||
)
|
)
|
||||||
// Enrich products with stall name
|
// Enrich products with stall name and missing properties to match Product interface
|
||||||
const enrichedProducts = (products || []).map(product => ({
|
const enrichedProducts = (products || []).map(product => ({
|
||||||
...product,
|
id: product.id || `${product.stall_id}-${Date.now()}`, // Ensure id is always string
|
||||||
stallName: activeStall.value?.name || 'Unknown Stall'
|
stall_id: product.stall_id,
|
||||||
|
stallName: activeStall.value?.name || 'Unknown Stall',
|
||||||
|
name: product.name,
|
||||||
|
description: product.config?.description || '',
|
||||||
|
price: product.price,
|
||||||
|
currency: activeStall.value?.currency || 'sats', // Use stall currency
|
||||||
|
quantity: product.quantity,
|
||||||
|
images: product.images,
|
||||||
|
categories: product.categories,
|
||||||
|
createdAt: product.event_created_at || Date.now(),
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
nostrEventId: product.event_id,
|
||||||
|
// API-specific properties that are expected by the template
|
||||||
|
active: product.active ?? true,
|
||||||
|
pending: product.pending ?? false,
|
||||||
|
config: product.config || { currency: activeStall.value?.currency || 'sats' }
|
||||||
}))
|
}))
|
||||||
stallProducts.value = enrichedProducts
|
stallProducts.value = enrichedProducts
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,10 @@ export interface Product {
|
||||||
createdAt: number
|
createdAt: number
|
||||||
updatedAt: number
|
updatedAt: number
|
||||||
nostrEventId?: string
|
nostrEventId?: string
|
||||||
|
// API-specific properties for merchant store management
|
||||||
|
active?: boolean
|
||||||
|
pending?: boolean
|
||||||
|
config?: { currency?: string, [key: string]: any }
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Order {
|
export interface Order {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
<!-- Enhanced Fuzzy Search Bar - Full Width on Mobile -->
|
<!-- Enhanced Fuzzy Search Bar - Full Width on Mobile -->
|
||||||
<div class="w-full lg:flex-1 lg:max-w-md">
|
<div class="w-full lg:flex-1 lg:max-w-md">
|
||||||
<MarketFuzzySearch
|
<MarketFuzzySearch
|
||||||
:data="marketStore.products"
|
:data="marketStore.products as Product[]"
|
||||||
:options="searchOptions"
|
:options="searchOptions"
|
||||||
@results="handleSearchResults"
|
@results="handleSearchResults"
|
||||||
@filter-category="handleCategoryFilter"
|
@filter-category="handleCategoryFilter"
|
||||||
|
|
@ -193,7 +193,7 @@
|
||||||
<ProductCard
|
<ProductCard
|
||||||
v-for="product in productsToDisplay"
|
v-for="product in productsToDisplay"
|
||||||
:key="product.id"
|
:key="product.id"
|
||||||
:product="product"
|
:product="product as Product"
|
||||||
@add-to-cart="addToCart"
|
@add-to-cart="addToCart"
|
||||||
@view-details="viewProduct"
|
@view-details="viewProduct"
|
||||||
@view-stall="viewStall"
|
@view-stall="viewStall"
|
||||||
|
|
@ -222,7 +222,7 @@ import { config } from '@/lib/config'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
|
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
|
||||||
import { ShoppingCart, X, Check, Filter } from 'lucide-vue-next'
|
import { ShoppingCart, X, Check } from 'lucide-vue-next'
|
||||||
import MarketFuzzySearch from '../components/MarketFuzzySearch.vue'
|
import MarketFuzzySearch from '../components/MarketFuzzySearch.vue'
|
||||||
import ProductCard from '../components/ProductCard.vue'
|
import ProductCard from '../components/ProductCard.vue'
|
||||||
import type { Product } from '../types/market'
|
import type { Product } from '../types/market'
|
||||||
|
|
@ -237,22 +237,19 @@ const marketPreloader = useMarketPreloader()
|
||||||
const productsForCategoryFilter = computed(() => {
|
const productsForCategoryFilter = computed(() => {
|
||||||
return searchResults.value.length > 0
|
return searchResults.value.length > 0
|
||||||
? searchResults.value
|
? searchResults.value
|
||||||
: marketStore.products
|
: (marketStore.products as Product[])
|
||||||
})
|
})
|
||||||
|
|
||||||
// Category filtering with optimized composable
|
// Category filtering with optimized composable
|
||||||
const {
|
const {
|
||||||
allCategories,
|
allCategories,
|
||||||
filteredProducts: categoryFilteredProducts,
|
|
||||||
selectedCount: selectedCategoriesCount,
|
selectedCount: selectedCategoriesCount,
|
||||||
selectedCategoryNames: selectedCategories,
|
selectedCategoryNames: selectedCategories,
|
||||||
hasActiveFilters,
|
hasActiveFilters,
|
||||||
filterMode,
|
filterMode,
|
||||||
toggleCategory,
|
toggleCategory,
|
||||||
clearAllCategories: clearAllCategoryFilters,
|
clearAllCategories: clearAllCategoryFilters,
|
||||||
setFilterMode,
|
setFilterMode
|
||||||
toggleFilterMode,
|
|
||||||
categoryStats
|
|
||||||
} = useCategoryFilter(productsForCategoryFilter)
|
} = useCategoryFilter(productsForCategoryFilter)
|
||||||
|
|
||||||
let unsubscribe: (() => void) | null = null
|
let unsubscribe: (() => void) | null = null
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@
|
||||||
<ProductCard
|
<ProductCard
|
||||||
v-for="product in filteredProducts"
|
v-for="product in filteredProducts"
|
||||||
:key="product.id"
|
:key="product.id"
|
||||||
:product="product"
|
:product="product as Product"
|
||||||
@view-details="viewProductDetails"
|
@view-details="viewProductDetails"
|
||||||
@view-stall="viewStall"
|
@view-stall="viewStall"
|
||||||
/>
|
/>
|
||||||
|
|
@ -159,9 +159,8 @@
|
||||||
import { ref, computed, onMounted, watch } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useMarketStore } from '@/modules/market/stores/market'
|
import { useMarketStore } from '@/modules/market/stores/market'
|
||||||
import { Card, CardContent } from '@/components/ui/card'
|
import { Card } from '@/components/ui/card'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Input } from '@/components/ui/input'
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
|
|
@ -184,6 +183,7 @@ const marketStore = useMarketStore()
|
||||||
// State
|
// State
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const searchResults = ref<Product[]>([])
|
const searchResults = ref<Product[]>([])
|
||||||
|
const searchQuery = ref('')
|
||||||
const sortBy = ref('name')
|
const sortBy = ref('name')
|
||||||
const selectedCategories = ref<string[]>([])
|
const selectedCategories = ref<string[]>([])
|
||||||
const showProductDetail = ref(false)
|
const showProductDetail = ref(false)
|
||||||
|
|
@ -213,8 +213,8 @@ const searchOptions: FuzzySearchOptions<Product> = {
|
||||||
const stallId = computed(() => route.params.stallId as string)
|
const stallId = computed(() => route.params.stallId as string)
|
||||||
|
|
||||||
// Get stall data
|
// Get stall data
|
||||||
const stall = computed<Stall | undefined>(() => {
|
const stall = computed(() => {
|
||||||
return marketStore.stalls.find(s => s.id === stallId.value)
|
return marketStore.stalls.find(s => s.id === stallId.value) as Stall | undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
// Get products for this stall
|
// Get products for this stall
|
||||||
|
|
@ -284,11 +284,14 @@ const toggleCategoryFilter = (category: string) => {
|
||||||
const clearFilters = () => {
|
const clearFilters = () => {
|
||||||
selectedCategories.value = []
|
selectedCategories.value = []
|
||||||
searchResults.value = []
|
searchResults.value = []
|
||||||
|
searchQuery.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle fuzzy search results
|
// Handle fuzzy search results
|
||||||
const handleSearchResults = (results: Product[]) => {
|
const handleSearchResults = (results: Product[]) => {
|
||||||
searchResults.value = results
|
searchResults.value = results
|
||||||
|
// Extract search query from fuzzy search component if needed
|
||||||
|
// For now, we'll track it separately
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewProductDetails = (product: Product) => {
|
const viewProductDetails = (product: Product) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue