feat: Format event price and wallet balance in PurchaseTicketDialog and events page
- Implemented formatting functions for event ticket prices and wallet balances to enhance readability and user experience. - Updated PurchaseTicketDialog.vue and events.vue to utilize the new formatting functions, ensuring consistent display of financial information.
This commit is contained in:
parent
147cf31f0f
commit
df7e461c91
4 changed files with 287 additions and 4 deletions
|
|
@ -7,6 +7,7 @@ import { Badge } from '@/components/ui/badge'
|
||||||
import { useTicketPurchase } from '@/composables/useTicketPurchase'
|
import { useTicketPurchase } from '@/composables/useTicketPurchase'
|
||||||
import { useAuth } from '@/composables/useAuth'
|
import { useAuth } from '@/composables/useAuth'
|
||||||
import { User, Wallet, CreditCard, Zap, Ticket } from 'lucide-vue-next'
|
import { User, Wallet, CreditCard, Zap, Ticket } from 'lucide-vue-next'
|
||||||
|
import { formatEventPrice, formatWalletBalance } from '@/lib/utils/formatting'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
event: {
|
event: {
|
||||||
|
|
@ -76,7 +77,7 @@ onUnmounted(() => {
|
||||||
Purchase Ticket
|
Purchase Ticket
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Purchase a ticket for <strong>{{ event.name }}</strong> for {{ event.price_per_ticket }} {{ event.currency }}
|
Purchase a ticket for <strong>{{ event.name }}</strong> for {{ formatEventPrice(event.price_per_ticket, event.currency) }}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
|
|
@ -130,7 +131,7 @@ onUnmounted(() => {
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-sm font-medium">{{ wallet.name }}</span>
|
<span class="text-sm font-medium">{{ wallet.name }}</span>
|
||||||
<Badge v-if="wallet.balance_msat > 0" variant="default" class="text-xs">
|
<Badge v-if="wallet.balance_msat > 0" variant="default" class="text-xs">
|
||||||
{{ (wallet.balance_msat / 1000).toFixed(0) }} sats
|
{{ formatWalletBalance(wallet.balance_msat) }}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge v-else variant="secondary" class="text-xs">Empty</Badge>
|
<Badge v-else variant="secondary" class="text-xs">Empty</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -158,7 +159,7 @@ onUnmounted(() => {
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<span class="text-sm text-muted-foreground">Price:</span>
|
<span class="text-sm text-muted-foreground">Price:</span>
|
||||||
<span class="text-sm font-medium">{{ event.price_per_ticket }} {{ event.currency }}</span>
|
<span class="text-sm font-medium">{{ formatEventPrice(event.price_per_ticket, event.currency) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
201
src/lib/utils/formatting.test.ts
Normal file
201
src/lib/utils/formatting.test.ts
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
/**
|
||||||
|
* Test file for formatting utility functions
|
||||||
|
* This file can be run with a test runner or used for manual verification
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
formatNumber,
|
||||||
|
formatSats,
|
||||||
|
formatMsats,
|
||||||
|
formatCurrency,
|
||||||
|
formatEventPrice,
|
||||||
|
formatWalletBalance
|
||||||
|
} from './formatting'
|
||||||
|
|
||||||
|
// Test data
|
||||||
|
const testNumbers = [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
999,
|
||||||
|
1000,
|
||||||
|
1001,
|
||||||
|
9999,
|
||||||
|
10000,
|
||||||
|
10001,
|
||||||
|
99999,
|
||||||
|
100000,
|
||||||
|
100001,
|
||||||
|
999999,
|
||||||
|
1000000,
|
||||||
|
1000001,
|
||||||
|
1234567,
|
||||||
|
9999999,
|
||||||
|
10000000
|
||||||
|
]
|
||||||
|
|
||||||
|
const testSats = [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
999,
|
||||||
|
1000,
|
||||||
|
1001,
|
||||||
|
9999,
|
||||||
|
10000,
|
||||||
|
10001,
|
||||||
|
99999,
|
||||||
|
100000,
|
||||||
|
100001,
|
||||||
|
999999,
|
||||||
|
1000000,
|
||||||
|
1000001,
|
||||||
|
1234567,
|
||||||
|
9999999,
|
||||||
|
10000000
|
||||||
|
]
|
||||||
|
|
||||||
|
const testMsats = [
|
||||||
|
0,
|
||||||
|
1000,
|
||||||
|
999000,
|
||||||
|
1000000,
|
||||||
|
1001000,
|
||||||
|
9999000,
|
||||||
|
10000000,
|
||||||
|
10001000,
|
||||||
|
99999000,
|
||||||
|
100000000,
|
||||||
|
100001000,
|
||||||
|
999999000,
|
||||||
|
1000000000,
|
||||||
|
1000001000,
|
||||||
|
1234567000,
|
||||||
|
9999999000,
|
||||||
|
10000000000
|
||||||
|
]
|
||||||
|
|
||||||
|
const testCurrencies = [
|
||||||
|
{ amount: 0, currency: 'USD' },
|
||||||
|
{ amount: 1.99, currency: 'USD' },
|
||||||
|
{ amount: 999.99, currency: 'USD' },
|
||||||
|
{ amount: 1000, currency: 'USD' },
|
||||||
|
{ amount: 1001.50, currency: 'USD' },
|
||||||
|
{ amount: 9999.99, currency: 'USD' },
|
||||||
|
{ amount: 10000, currency: 'USD' },
|
||||||
|
{ amount: 0, currency: 'EUR' },
|
||||||
|
{ amount: 1.99, currency: 'EUR' },
|
||||||
|
{ amount: 999.99, currency: 'EUR' },
|
||||||
|
{ amount: 1000, currency: 'EUR' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const testEventPrices = [
|
||||||
|
{ price: 0, currency: 'sats' },
|
||||||
|
{ price: 1, currency: 'sats' },
|
||||||
|
{ price: 999, currency: 'sats' },
|
||||||
|
{ price: 1000, currency: 'sats' },
|
||||||
|
{ price: 1001, currency: 'sats' },
|
||||||
|
{ price: 9999, currency: 'sats' },
|
||||||
|
{ price: 10000, currency: 'sats' },
|
||||||
|
{ price: 0, currency: 'USD' },
|
||||||
|
{ price: 1.99, currency: 'USD' },
|
||||||
|
{ price: 999.99, currency: 'USD' },
|
||||||
|
{ price: 1000, currency: 'USD' },
|
||||||
|
{ price: 0, currency: 'EUR' },
|
||||||
|
{ price: 1.99, currency: 'EUR' },
|
||||||
|
{ price: 999.99, currency: 'EUR' },
|
||||||
|
{ price: 1000, currency: 'EUR' }
|
||||||
|
]
|
||||||
|
|
||||||
|
// Test functions
|
||||||
|
function testFormatNumber() {
|
||||||
|
console.log('Testing formatNumber function:')
|
||||||
|
testNumbers.forEach(num => {
|
||||||
|
const formatted = formatNumber(num)
|
||||||
|
console.log(`${num} -> "${formatted}"`)
|
||||||
|
})
|
||||||
|
console.log('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFormatSats() {
|
||||||
|
console.log('Testing formatSats function:')
|
||||||
|
testSats.forEach(sats => {
|
||||||
|
const formatted = formatSats(sats)
|
||||||
|
console.log(`${sats} sats -> "${formatted}"`)
|
||||||
|
})
|
||||||
|
console.log('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFormatMsats() {
|
||||||
|
console.log('Testing formatMsats function:')
|
||||||
|
testMsats.forEach(msats => {
|
||||||
|
const formatted = formatMsats(msats)
|
||||||
|
console.log(`${msats} msats -> "${formatted}"`)
|
||||||
|
})
|
||||||
|
console.log('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFormatCurrency() {
|
||||||
|
console.log('Testing formatCurrency function:')
|
||||||
|
testCurrencies.forEach(({ amount, currency }) => {
|
||||||
|
const formatted = formatCurrency(amount, currency)
|
||||||
|
console.log(`${amount} ${currency} -> "${formatted}"`)
|
||||||
|
})
|
||||||
|
console.log('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFormatEventPrice() {
|
||||||
|
console.log('Testing formatEventPrice function:')
|
||||||
|
testEventPrices.forEach(({ price, currency }) => {
|
||||||
|
const formatted = formatEventPrice(price, currency)
|
||||||
|
console.log(`${price} ${currency} -> "${formatted}"`)
|
||||||
|
})
|
||||||
|
console.log('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFormatWalletBalance() {
|
||||||
|
console.log('Testing formatWalletBalance function:')
|
||||||
|
testMsats.forEach(msats => {
|
||||||
|
const formatted = formatWalletBalance(msats)
|
||||||
|
console.log(`${msats} msats -> "${formatted}"`)
|
||||||
|
})
|
||||||
|
console.log('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all tests
|
||||||
|
function runAllTests() {
|
||||||
|
console.log('=== FORMATTING UTILITY TESTS ===\n')
|
||||||
|
|
||||||
|
testFormatNumber()
|
||||||
|
testFormatSats()
|
||||||
|
testFormatMsats()
|
||||||
|
testFormatCurrency()
|
||||||
|
testFormatEventPrice()
|
||||||
|
testFormatWalletBalance()
|
||||||
|
|
||||||
|
console.log('=== TESTS COMPLETED ===')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for manual testing
|
||||||
|
export {
|
||||||
|
runAllTests,
|
||||||
|
testFormatNumber,
|
||||||
|
testFormatSats,
|
||||||
|
testFormatMsats,
|
||||||
|
testFormatCurrency,
|
||||||
|
testFormatEventPrice,
|
||||||
|
testFormatWalletBalance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run tests if this file is executed directly
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// Browser environment - add to window for console testing
|
||||||
|
(window as any).formattingTests = {
|
||||||
|
runAllTests,
|
||||||
|
testFormatNumber,
|
||||||
|
testFormatSats,
|
||||||
|
testFormatMsats,
|
||||||
|
testFormatCurrency,
|
||||||
|
testFormatEventPrice,
|
||||||
|
testFormatWalletBalance
|
||||||
|
}
|
||||||
|
console.log('Formatting tests available at window.formattingTests')
|
||||||
|
}
|
||||||
80
src/lib/utils/formatting.ts
Normal file
80
src/lib/utils/formatting.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
* Formatting utility functions for amounts and currencies
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a number with thousand separators
|
||||||
|
* @param value - The number to format
|
||||||
|
* @param locale - The locale to use for formatting (default: 'en-US')
|
||||||
|
* @returns Formatted string with thousand separators
|
||||||
|
*/
|
||||||
|
export function formatNumber(value: number, locale: string = 'en-US'): string {
|
||||||
|
return new Intl.NumberFormat(locale).format(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format satoshi amounts with thousand separators
|
||||||
|
* @param sats - The satoshi amount
|
||||||
|
* @param locale - The locale to use for formatting (default: 'en-US')
|
||||||
|
* @returns Formatted string with "sats" suffix
|
||||||
|
*/
|
||||||
|
export function formatSats(sats: number, locale: string = 'en-US'): string {
|
||||||
|
return `${formatNumber(sats, locale)} sats`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format millisatoshi amounts with thousand separators
|
||||||
|
* @param msats - The millisatoshi amount
|
||||||
|
* @param locale - The locale to use for formatting (default: 'en-US')
|
||||||
|
* @returns Formatted string with "sats" suffix
|
||||||
|
*/
|
||||||
|
export function formatMsats(msats: number, locale: string = 'en-US'): string {
|
||||||
|
const sats = Math.floor(msats / 1000)
|
||||||
|
return formatSats(sats, locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format currency amounts with thousand separators
|
||||||
|
* @param amount - The amount to format
|
||||||
|
* @param currency - The currency code (e.g., 'USD', 'EUR')
|
||||||
|
* @param locale - The locale to use for formatting (default: 'en-US')
|
||||||
|
* @returns Formatted currency string
|
||||||
|
*/
|
||||||
|
export function formatCurrency(amount: number, currency: string, locale: string = 'en-US'): string {
|
||||||
|
try {
|
||||||
|
return new Intl.NumberFormat(locale, {
|
||||||
|
style: 'currency',
|
||||||
|
currency: currency.toUpperCase()
|
||||||
|
}).format(amount)
|
||||||
|
} catch (error) {
|
||||||
|
// Fallback to basic formatting if currency is not supported
|
||||||
|
return `${formatNumber(amount, locale)} ${currency}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format event ticket price with appropriate formatting
|
||||||
|
* @param price - The price amount
|
||||||
|
* @param currency - The currency code
|
||||||
|
* @param locale - The locale to use for formatting (default: 'en-US')
|
||||||
|
* @returns Formatted price string
|
||||||
|
*/
|
||||||
|
export function formatEventPrice(price: number, currency: string, locale: string = 'en-US'): string {
|
||||||
|
if (currency.toLowerCase() === 'sats' || currency.toLowerCase() === 'sat') {
|
||||||
|
return formatSats(price, locale)
|
||||||
|
}
|
||||||
|
return formatCurrency(price, currency, locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format wallet balance with appropriate formatting
|
||||||
|
* @param balanceMsat - The balance in millisatoshi
|
||||||
|
* @param locale - The locale to use for formatting (default: 'en-US')
|
||||||
|
* @returns Formatted balance string
|
||||||
|
*/
|
||||||
|
export function formatWalletBalance(balanceMsat: number, locale: string = 'en-US'): string {
|
||||||
|
if (balanceMsat === 0) {
|
||||||
|
return '0 sats'
|
||||||
|
}
|
||||||
|
return formatMsats(balanceMsat, locale)
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import { Badge } from '@/components/ui/badge'
|
||||||
import { format } from 'date-fns'
|
import { format } from 'date-fns'
|
||||||
import PurchaseTicketDialog from '@/components/events/PurchaseTicketDialog.vue'
|
import PurchaseTicketDialog from '@/components/events/PurchaseTicketDialog.vue'
|
||||||
import { RefreshCw, User, LogIn } from 'lucide-vue-next'
|
import { RefreshCw, User, LogIn } from 'lucide-vue-next'
|
||||||
|
import { formatEventPrice } from '@/lib/utils/formatting'
|
||||||
|
|
||||||
const { upcomingEvents, pastEvents, isLoading, error, refresh } = useEvents()
|
const { upcomingEvents, pastEvents, isLoading, error, refresh } = useEvents()
|
||||||
const { isAuthenticated, userDisplay } = useAuth()
|
const { isAuthenticated, userDisplay } = useAuth()
|
||||||
|
|
@ -99,7 +100,7 @@ function handlePurchaseClick(event: {
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<span class="text-muted-foreground">Price:</span>
|
<span class="text-muted-foreground">Price:</span>
|
||||||
<span class="text-foreground">{{ event.price_per_ticket }} {{ event.currency }}</span>
|
<span class="text-foreground">{{ formatEventPrice(event.price_per_ticket, event.currency) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue