Enhance market module with new chat and events features
- Introduce chat module with components, services, and composables for real-time messaging. - Implement events module with API service, components, and ticket purchasing functionality. - Update app configuration to include new modules and their respective settings. - Refactor existing components to integrate with the new chat and events features. - Enhance market store and services to support new functionalities and improve order management. - Update routing to accommodate new views for chat and events, ensuring seamless navigation.
This commit is contained in:
parent
519a9003d4
commit
e40ac91417
46 changed files with 6305 additions and 3264 deletions
250
src/modules/market/components/ShoppingCart.vue
Normal file
250
src/modules/market/components/ShoppingCart.vue
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
<template>
|
||||
<div class="space-y-6">
|
||||
<!-- Cart Header -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-foreground">Shopping Cart</h2>
|
||||
<p class="text-muted-foreground">
|
||||
{{ totalCartItems }} items across {{ allStallCarts.length }} stalls
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="text-right">
|
||||
<p class="text-sm text-muted-foreground">Total Value</p>
|
||||
<p class="text-xl font-bold text-green-600">
|
||||
{{ formatPrice(totalCartValue, 'sats') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
v-if="allStallCarts.length > 0"
|
||||
@click="clearAllCarts"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
>
|
||||
Clear All
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty Cart State -->
|
||||
<div v-if="allStallCarts.length === 0" class="text-center py-12">
|
||||
<ShoppingCart class="mx-auto h-12 w-12 text-muted-foreground/50 mb-4" />
|
||||
<h3 class="text-lg font-medium text-foreground mb-2">Your cart is empty</h3>
|
||||
<p class="text-muted-foreground mb-6">Start shopping to add items to your cart</p>
|
||||
<Button @click="$router.push('/market')" variant="default">
|
||||
Continue Shopping
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Stall Carts -->
|
||||
<div v-else class="space-y-6">
|
||||
<div
|
||||
v-for="cart in allStallCarts"
|
||||
:key="cart.id"
|
||||
class="border border-border rounded-lg p-6 bg-card shadow-sm"
|
||||
>
|
||||
<!-- Stall Header -->
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 bg-primary/10 rounded-full flex items-center justify-center">
|
||||
<Store class="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-foreground">
|
||||
{{ getStallName(cart.id) }}
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{{ cart.products.length }} item{{ cart.products.length !== 1 ? 's' : '' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-right">
|
||||
<p class="text-sm text-muted-foreground">Stall Total</p>
|
||||
<p class="text-lg font-semibold text-green-600">
|
||||
{{ formatPrice(cart.subtotal, cart.currency) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cart Items -->
|
||||
<div class="space-y-3 mb-4">
|
||||
<CartItem
|
||||
v-for="item in cart.products"
|
||||
:key="item.product.id"
|
||||
:item="item"
|
||||
:stall-id="cart.id"
|
||||
@update-quantity="updateQuantity"
|
||||
@remove-item="removeItem"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Stall Cart Actions -->
|
||||
<div class="pt-4 border-t border-border">
|
||||
<!-- Desktop Layout -->
|
||||
<div class="hidden md:flex items-center justify-between">
|
||||
<div class="flex items-center space-x-4">
|
||||
<Button
|
||||
@click="clearStallCart(cart.id)"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
>
|
||||
Clear Stall
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@click="viewStall(cart.id)"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
>
|
||||
View Stall
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-3">
|
||||
<!-- Cart Summary for this stall -->
|
||||
<div class="text-right mr-4">
|
||||
<p class="text-sm text-muted-foreground">Total</p>
|
||||
<p class="text-lg font-semibold text-green-600">
|
||||
{{ formatPrice(cart.subtotal, cart.currency) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
@click="proceedToCheckout(cart.id)"
|
||||
:disabled="!canProceedToCheckout(cart.id)"
|
||||
variant="default"
|
||||
>
|
||||
Checkout
|
||||
<ArrowRight class="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Layout -->
|
||||
<div class="md:hidden space-y-4">
|
||||
<!-- Action Buttons Row -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Button
|
||||
@click="clearStallCart(cart.id)"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
>
|
||||
Clear Stall
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@click="viewStall(cart.id)"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
>
|
||||
View Stall
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Total and Checkout Row -->
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- Cart Summary for this stall -->
|
||||
<div class="text-left">
|
||||
<p class="text-sm text-muted-foreground">Total</p>
|
||||
<p class="text-lg font-semibold text-green-600">
|
||||
{{ formatPrice(cart.subtotal, cart.currency) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
@click="proceedToCheckout(cart.id)"
|
||||
:disabled="!canProceedToCheckout(cart.id)"
|
||||
variant="default"
|
||||
class="flex items-center"
|
||||
>
|
||||
Checkout
|
||||
<ArrowRight class="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Continue Shopping Button -->
|
||||
<div v-if="allStallCarts.length > 0" class="text-center mt-8">
|
||||
<Button @click="$router.push('/market')" variant="outline" size="lg">
|
||||
Continue Shopping
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMarketStore } from '@/stores/market'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
ShoppingCart,
|
||||
Store,
|
||||
ArrowRight
|
||||
} from 'lucide-vue-next'
|
||||
import CartItem from './CartItem.vue'
|
||||
|
||||
|
||||
const router = useRouter()
|
||||
const marketStore = useMarketStore()
|
||||
|
||||
// Computed properties
|
||||
const allStallCarts = computed(() => marketStore.allStallCarts)
|
||||
const totalCartItems = computed(() => marketStore.totalCartItems)
|
||||
const totalCartValue = computed(() => marketStore.totalCartValue)
|
||||
|
||||
// Methods
|
||||
const getStallName = (stallId: string) => {
|
||||
const stall = marketStore.stalls.find(s => s.id === stallId)
|
||||
return stall?.name || 'Unknown Stall'
|
||||
}
|
||||
|
||||
const updateQuantity = (stallId: string, productId: string, quantity: number) => {
|
||||
marketStore.updateStallCartQuantity(stallId, productId, quantity)
|
||||
}
|
||||
|
||||
const removeItem = (stallId: string, productId: string) => {
|
||||
marketStore.removeFromStallCart(stallId, productId)
|
||||
}
|
||||
|
||||
const clearStallCart = (stallId: string) => {
|
||||
marketStore.clearStallCart(stallId)
|
||||
}
|
||||
|
||||
const clearAllCarts = () => {
|
||||
marketStore.clearAllStallCarts()
|
||||
}
|
||||
|
||||
const viewStall = (stallId: string) => {
|
||||
// TODO: Navigate to stall page
|
||||
console.log('View stall:', stallId)
|
||||
}
|
||||
|
||||
const proceedToCheckout = (stallId: string) => {
|
||||
marketStore.setCheckoutCart(stallId)
|
||||
router.push(`/checkout/${stallId}`)
|
||||
}
|
||||
|
||||
const canProceedToCheckout = (stallId: string) => {
|
||||
const cart = marketStore.stallCarts[stallId]
|
||||
return cart && cart.products.length > 0
|
||||
}
|
||||
|
||||
const formatPrice = (price: number, currency: string) => {
|
||||
if (currency === 'sats' || currency === 'sat') {
|
||||
return `${price.toLocaleString()} sats`
|
||||
}
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: currency.toUpperCase()
|
||||
}).format(price)
|
||||
}
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue