({
)
}
- // Memoized displayed items
const list = useMemo(() => {
if (isProcessing) {
return []
diff --git a/components/Premium/BuySubscription/BuySubscription.tsx b/components/Premium/BuySubscription/BuySubscription.tsx
index 26b113f94..4ed746e58 100644
--- a/components/Premium/BuySubscription/BuySubscription.tsx
+++ b/components/Premium/BuySubscription/BuySubscription.tsx
@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { PREMIUM_TYPES } from '../../../utils/PremiumTypeUtils'
import api from '../../../api/ApiHelper'
-import { Button, Card, Col, Row, Form, Spinner, Alert, InputGroup } from 'react-bootstrap'
+import { Button, Card, Col, Row, Form, Spinner, InputGroup } from 'react-bootstrap'
import styles from './BuySubscription.module.css'
import stepStyles from '../PremiumPurchaseWizard/Steps/Steps.module.css'
import NumberElement from '../../Number/Number'
@@ -16,10 +16,8 @@ import {
getProviderCurrencyCode,
getDiscountPercent,
getTierApiProductId,
- getTierProductId,
- getFallbackSubscriptionPrice
-} from '../../../utils/pricingUtils'
-import { VAT_RATES } from '../../../utils/PricingUtils'
+ VAT_RATES
+} from '../../../utils/PricingUtils'
interface Props {
activePremiumProduct: PremiumProduct
@@ -255,33 +253,33 @@ function BuySubscription(props: Props) {
const getTotalDiscountInfo = (): { creatorPercent: number | null; discountCodePercent: number | null; totalSavings: number | null } => {
const creatorPercent = props.selectedTier ? getDiscountPercentValue(getProductIdForTier(props.selectedTier, getCurrentDuration())) : null
const discountCodePercent = validatedDiscount?.amountType === 'percent' ? validatedDiscount.amount : null
-
+
const originalPrice = getOriginalPrice()
const basePrice = getSubscriptionPrice()
const finalPrice = getFinalPrice(basePrice)
-
+
if (originalPrice && finalPrice < originalPrice) {
const totalSavings = Math.round((1 - finalPrice / originalPrice) * 100)
return { creatorPercent, discountCodePercent: discountCodePercent ?? null, totalSavings }
}
-
+
return { creatorPercent, discountCodePercent: discountCodePercent ?? null, totalSavings: null }
}
// If we have wizard selections, use them to determine the selected type and duration
const wizardSelectedType = props.selectedTier
? PREMIUM_TYPES.find(type => {
- switch (props.selectedTier) {
- case PremiumTier.PREMIUM:
- return type.productId === 'premium'
- case PremiumTier.PREMIUM_PLUS:
- return type.productId === 'premium_plus'
- case PremiumTier.STARTER:
- return type.productId === 'starter_premium'
- default:
- return type.productId === 'premium'
- }
- })
+ switch (props.selectedTier) {
+ case PremiumTier.PREMIUM:
+ return type.productId === 'premium'
+ case PremiumTier.PREMIUM_PLUS:
+ return type.productId === 'premium_plus'
+ case PremiumTier.STARTER:
+ return type.productId === 'starter_premium'
+ default:
+ return type.productId === 'premium'
+ }
+ })
: undefined
const wizardIsYearOption = props.selectedDuration === Duration.YEARLY
@@ -411,9 +409,8 @@ function BuySubscription(props: Props) {
Tier:{' '}
{getDisplayTierName()}
@@ -495,7 +492,6 @@ function BuySubscription(props: Props) {
- {/* Code Input Section - Side by side on desktop */}
@@ -531,8 +527,8 @@ function BuySubscription(props: Props) {
)}
{appliedCreatorCode && !pricingError && (
- {getDiscountPercentValue(getProductIdForTier(props.selectedTier!, getCurrentDuration()))
- ? `✓ Creator code applied! You get ${getDiscountPercentValue(getProductIdForTier(props.selectedTier!, getCurrentDuration()))}% off`
+ {getDiscountPercentValue(getProductIdForTier(props.selectedTier!, getCurrentDuration()))
+ ? `✓ Creator code applied! You get ${getDiscountPercentValue(getProductIdForTier(props.selectedTier!, getCurrentDuration()))}% off`
: '✓ Creator code applied'}
)}
@@ -639,7 +635,7 @@ function BuySubscription(props: Props) {
const activeEl = document.activeElement as HTMLElement | null
if (activeEl && activeEl.tagName === 'BUTTON') {
activeEl.innerText = 'Redirecting to payment provider...'
- ;(activeEl as HTMLButtonElement).disabled = true
+ ; (activeEl as HTMLButtonElement).disabled = true
}
}}
>
diff --git a/components/Premium/PremiumPurchaseWizard/PremiumPurchaseWizard.tsx b/components/Premium/PremiumPurchaseWizard/PremiumPurchaseWizard.tsx
index f7d3f3a5e..cd53c506a 100644
--- a/components/Premium/PremiumPurchaseWizard/PremiumPurchaseWizard.tsx
+++ b/components/Premium/PremiumPurchaseWizard/PremiumPurchaseWizard.tsx
@@ -25,7 +25,6 @@ function PremiumPurchaseWizard(props: Props) {
const totalSteps = 4
- // Get country code on mount
useEffect(() => {
if (typeof window !== 'undefined') {
const stored = localStorage.getItem('countryCode')
@@ -33,7 +32,6 @@ function PremiumPurchaseWizard(props: Props) {
}
}, [])
- // Helper function to get current tier from active premium product
const getCurrentTier = (): PremiumTier | null => {
if (!props.activePremiumProduct) return null
@@ -45,7 +43,6 @@ function PremiumPurchaseWizard(props: Props) {
return null
}
- // Helper function to get tier rank for comparison
const getTierRank = (tier: PremiumTier): number => {
switch (tier) {
case PremiumTier.STARTER:
@@ -59,12 +56,10 @@ function PremiumPurchaseWizard(props: Props) {
}
}
- // Helper function to check if user has active premium
const hasActivePremium = (): boolean => {
return props.activePremiumProduct && props.activePremiumProduct.expires.getTime() > new Date().getTime()
}
- // Helper function to get suggested upgrade tier
const getSuggestedUpgradeTier = (): PremiumTier | null => {
const currentTier = getCurrentTier()
if (!currentTier) return null
@@ -81,7 +76,6 @@ function PremiumPurchaseWizard(props: Props) {
}
}
- // Initialize wizard based on URL parameters and current state
useEffect(() => {
const urlParams = new URLSearchParams(window.location.search)
const tierParam = urlParams.get('tier')
@@ -92,13 +86,11 @@ function PremiumPurchaseWizard(props: Props) {
setUrlDiscountCode(codeParam)
}
- // If tier is specified in URL, pre-select it
const preSelectedTier = parseTierFromUrl(tierParam)
if (preSelectedTier) {
setSelectedTier(preSelectedTier)
- // Skip tier selection step if it's premium+ or if user already has a lower tier
const currentTier = getCurrentTier()
const currentTierRank = currentTier ? getTierRank(currentTier) : 0
const selectedTierRank = getTierRank(preSelectedTier)
@@ -107,7 +99,6 @@ function PremiumPurchaseWizard(props: Props) {
setCurrentStep(2) // Skip to payment method step
}
} else if (hasActivePremium()) {
- // If user has active premium but no tier specified, suggest upgrade
const suggestedTier = getSuggestedUpgradeTier()
if (suggestedTier) {
setSelectedTier(suggestedTier)
@@ -122,9 +113,8 @@ function PremiumPurchaseWizard(props: Props) {
switch (currentStep) {
case 1:
if (isUpgrade) {
- return `Upgrade Your ${
- currentTier === PremiumTier.STARTER ? 'Starter Premium' : currentTier === PremiumTier.PREMIUM ? 'Premium' : 'Premium Plus'
- }`
+ return `Upgrade Your ${currentTier === PremiumTier.STARTER ? 'Starter Premium' : currentTier === PremiumTier.PREMIUM ? 'Premium' : 'Premium Plus'
+ }`
}
return 'Choose Your Premium Tier'
case 2:
diff --git a/components/Premium/PremiumPurchaseWizard/Steps/DurationSelectionStep.tsx b/components/Premium/PremiumPurchaseWizard/Steps/DurationSelectionStep.tsx
index adc8e30fd..12d66d54a 100644
--- a/components/Premium/PremiumPurchaseWizard/Steps/DurationSelectionStep.tsx
+++ b/components/Premium/PremiumPurchaseWizard/Steps/DurationSelectionStep.tsx
@@ -1,10 +1,8 @@
'use client'
import { Card } from 'react-bootstrap'
-import { CheckCircle } from '@mui/icons-material'
import styles from './Steps.module.css'
import { Duration, PurchaseType, PremiumTier, getTierDisplayName, getDurationOptions } from '../types'
-import { SUBSCRIPTION_PRICES } from '../../../../utils/pricingUtils'
-import { VAT_RATES } from '../../../../utils/PricingUtils'
+import { VAT_RATES, SUBSCRIPTION_PRICES } from '../../../../utils/PricingUtils'
interface Props {
selectedType: PurchaseType
@@ -14,10 +12,10 @@ interface Props {
countryCode?: string
}
-export default function DurationSelectionStep({
- selectedType,
- selectedTier,
- selectedDuration,
+export default function DurationSelectionStep({
+ selectedType,
+ selectedTier,
+ selectedDuration,
onDurationSelect,
countryCode
}: Props) {
@@ -59,10 +57,10 @@ export default function DurationSelectionStep({
return getPriceWithVAT(monthlyPrice)
}
- // Round up to next full cent
- const roundUpToCent = (value: number): number => {
- return Math.ceil(value * 100) / 100
- }
+ // Round up to next full cent
+ const roundUpToCent = (value: number): number => {
+ return Math.ceil(value * 100) / 100
+ }
if (selectedType === PurchaseType.COFLCOINS) {
return (
@@ -70,9 +68,8 @@ export default function DurationSelectionStep({
How long would you like your{' '}
{tierDisplayName}
@@ -81,9 +78,8 @@ export default function DurationSelectionStep({
Choose your preferred duration for{' '}
{tierDisplayName}
diff --git a/components/Premium/PremiumPurchaseWizard/Steps/PurchaseCompletionStep.tsx b/components/Premium/PremiumPurchaseWizard/Steps/PurchaseCompletionStep.tsx
index 65cf45ddf..e5d590617 100644
--- a/components/Premium/PremiumPurchaseWizard/Steps/PurchaseCompletionStep.tsx
+++ b/components/Premium/PremiumPurchaseWizard/Steps/PurchaseCompletionStep.tsx
@@ -2,7 +2,7 @@
import BuySubscription from '../../BuySubscription/BuySubscription'
import BuyPremium from '../../BuyPremium/BuyPremium'
import styles from './Steps.module.css'
-import { Duration, PurchaseType, PremiumTier, getTierDisplayName, getDurationDisplayName } from '../types'
+import { Duration, PurchaseType, PremiumTier } from '../types'
interface Props {
selectedTier: PremiumTier
@@ -28,9 +28,9 @@ export default function PurchaseCompletionStep({
return (
{selectedType === PurchaseType.SUBSCRIPTION && (
-
{
switch (tier) {
case PremiumTier.STARTER:
@@ -124,18 +122,15 @@ export default function TierSelectionStep({ selectedTier, onTierSelect, currentT
}
}
- // Helper function to check if tier can be selected (upgrade only)
const canSelectTier = (tier: PremiumTier): boolean => {
if (!isUpgrade || !currentTier) return true
return getTierRank(tier) > getTierRank(currentTier)
}
- // Helper function to check if tier should be highlighted as suggested
const isSuggestedTier = (tier: PremiumTier): boolean => {
return suggestedTier === tier
}
- // Helper function to get tier display status
const getTierStatus = (tier: PremiumTier): string => {
if (!isUpgrade || !currentTier) return ''
@@ -148,7 +143,6 @@ export default function TierSelectionStep({ selectedTier, onTierSelect, currentT
return 'higher-upgrade'
}
- // Format expiry date
const formatExpiryDate = (date: Date): string => {
return date.toLocaleDateString('en-US', {
year: 'numeric',
diff --git a/cypress/e2e/filter.cy.ts b/cypress/e2e/filter.cy.ts
index 948443f8a..5723e1309 100644
--- a/cypress/e2e/filter.cy.ts
+++ b/cypress/e2e/filter.cy.ts
@@ -8,8 +8,8 @@ describe('Item page', () => {
cy.visit('/item/ASPECT_OF_THE_DRAGON')
cy.contains('Add Filter').click()
cy.get('input[placeholder="Add filter"]').type('shar')
- cy.contains('a', 'Sharpness').click()
- cy.get('form').contains('SharpnessNone1234567Please fill the filter or remove it').find('input').type('5')
+ cy.contains('a', /sharpness/i).click()
+ cy.get('form').contains(/SharpnessNone1234567Please fill the filter or remove it/i).find('input').type('5')
cy.contains(/ended.*ago/i).click()
cy.url().should('match', /.*\/auction\/.*/i)
cy.contains('Sharpness 5').should('be.visible')
diff --git a/utils/PricingUtils.tsx b/utils/PricingUtils.tsx
index ab980abeb..2a3bd77c9 100644
--- a/utils/PricingUtils.tsx
+++ b/utils/PricingUtils.tsx
@@ -1,4 +1,6 @@
import { PremiumTier } from '../components/Premium/PremiumPurchaseWizard/types'
+import { BatchProductPricingResponse, ProviderPricingOption } from '../api/_generated/skyApi.schemas'
+import { Duration } from '../components/Premium/PremiumPurchaseWizard/types'
// Base prices in EUR (before tax)
export const BASE_PRICES = {
@@ -113,15 +115,131 @@ export function calculatePrice(tier: PremiumTier, countryCode?: string, discount
}
}
-export function getPricingPeriodText(tier: PremiumTier): string {
- switch (tier) {
- case PremiumTier.STARTER:
- return 'per year'
- case PremiumTier.PREMIUM:
- return 'per month'
- case PremiumTier.PREMIUM_PLUS:
- return 'one-time'
- default:
- return ''
+export const CURRENCY_SYMBOLS: Record = {
+ 'USD': '$',
+ 'EUR': '€',
+ 'GBP': '£',
+ 'INR': '₹',
+ 'JPY': '¥',
+ 'CNY': '¥',
+ 'CAD': 'C$',
+ 'AUD': 'A$'
+}
+
+export const getCurrencySymbol = (currencyCode: string): string => {
+ return CURRENCY_SYMBOLS[currencyCode] || currencyCode
+}
+
+export const getProvider = (
+ pricingData: BatchProductPricingResponse | null,
+ productSlug: string,
+ providerSlug: string
+): ProviderPricingOption | undefined => {
+ const product = pricingData?.products?.find(p => p.productSlug === productSlug)
+ return product?.providers?.find(p => p.providerSlug === providerSlug)
+}
+
+export const getProviderPrice = (
+ pricingData: BatchProductPricingResponse | null,
+ productSlug: string,
+ providerSlug: string
+): number | null => {
+ const provider = getProvider(pricingData, productSlug, providerSlug)
+ return provider ? (provider.discountedPrice ?? provider.originalPrice) : null
+}
+
+export const getProviderOriginalPrice = (
+ pricingData: BatchProductPricingResponse | null,
+ productSlug: string,
+ providerSlug: string
+): number | null => {
+ return getProvider(pricingData, productSlug, providerSlug)?.originalPrice ?? null
+}
+
+export const getProviderCurrencyCode = (
+ pricingData: BatchProductPricingResponse | null,
+ productSlug: string,
+ providerSlug: string
+): string => {
+ return getProvider(pricingData, productSlug, providerSlug)?.currencyCode ?? 'EUR'
+}
+
+export const getDiscountPercent = (
+ pricingData: BatchProductPricingResponse | null,
+ productSlug: string
+): number | null => {
+ return pricingData?.products?.find(p => p.productSlug === productSlug)?.discountPercent ?? null
+}
+
+const TIER_PRODUCT_MAP: Record = {
+ [PremiumTier.PREMIUM]: 'premium',
+ [PremiumTier.PREMIUM_PLUS]: 'premium_plus',
+ [PremiumTier.STARTER]: 'starter_premium'
+}
+
+export const getTierProductId = (tier: PremiumTier): string => {
+ return TIER_PRODUCT_MAP[tier] ?? 'premium'
+}
+
+const TIER_SLUG_MAP: Record = {
+ [PremiumTier.PREMIUM]: { monthly: 'l_premium', yearly: 'l_premium-year' },
+ [PremiumTier.PREMIUM_PLUS]: { monthly: 'l_prem_plus', yearly: 'l_prem_plus-year' },
+ [PremiumTier.STARTER]: { monthly: 'l_starter_premium', yearly: 'l_starter_premium' }
+}
+
+export const getTierSubscriptionSlug = (tier: PremiumTier, isYearly: boolean): string => {
+ const slugs = TIER_SLUG_MAP[tier]
+ return isYearly ? slugs.yearly : slugs.monthly
+}
+
+const TIER_API_PRODUCT_MAP: Record = {
+ [PremiumTier.PREMIUM]: 'l_premium',
+ [PremiumTier.PREMIUM_PLUS]: 'l_prem_plus',
+ [PremiumTier.STARTER]: 'l_starter_premium'
+}
+
+export const getTierApiProductId = (tier: PremiumTier, isYearlyOrDuration: boolean | Duration = false): string => {
+ const baseId = TIER_API_PRODUCT_MAP[tier]
+
+ if (typeof isYearlyOrDuration === 'string') {
+ switch (isYearlyOrDuration) {
+ case Duration.YEARLY:
+ return tier !== PremiumTier.STARTER ? `${baseId}-year` : baseId
+ case Duration.QUARTER:
+ return tier !== PremiumTier.STARTER ? `${baseId}-quarter` : baseId
+ case Duration.MONTHLY:
+ default:
+ return baseId
+ }
}
+
+ // Handle legacy boolean
+ const isYearly = isYearlyOrDuration as boolean
+ return isYearly && tier !== PremiumTier.STARTER ? `${baseId}-year` : baseId
+}
+
+export const SUBSCRIPTION_PRICES: Record = {
+ 'premium': { monthly: 9.69, quarterly: 27.69, yearly: 96.69 },
+ 'premium_plus': { monthly: 35.69, quarterly: 99.69, yearly: 354.2 },
+ 'starter_premium': { monthly: 16.99, quarterly: 16.99, yearly: 16.99 }
}
+
+export const getFallbackSubscriptionPrice = (productId: string, isYearly: boolean | Duration = false): number => {
+ const prices = SUBSCRIPTION_PRICES[productId]
+ if (!prices) return -1
+
+ if (typeof isYearly === 'string') {
+ switch (isYearly) {
+ case Duration.YEARLY:
+ return prices.yearly
+ case Duration.QUARTER:
+ return prices.quarterly
+ case Duration.MONTHLY:
+ default:
+ return prices.monthly
+ }
+ }
+
+ // Handle legacy boolean
+ return (isYearly as boolean) ? prices.yearly : prices.monthly
+}
\ No newline at end of file
diff --git a/utils/pricingUtils.ts b/utils/pricingUtils.ts
deleted file mode 100644
index 45afa3b2d..000000000
--- a/utils/pricingUtils.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-import { BatchProductPricingResponse, ProviderPricingOption } from '../api/_generated/skyApi.schemas'
-import { Duration, PremiumTier } from '../components/Premium/PremiumPurchaseWizard/types'
-
-// Currency symbol mapping
-export const CURRENCY_SYMBOLS: Record = {
- 'USD': '$',
- 'EUR': '€',
- 'GBP': '£',
- 'INR': '₹',
- 'JPY': '¥',
- 'CNY': '¥',
- 'CAD': 'C$',
- 'AUD': 'A$'
-}
-
-// Get currency symbol or code
-export const getCurrencySymbol = (currencyCode: string): string => {
- return CURRENCY_SYMBOLS[currencyCode] || currencyCode
-}
-
-// Find a provider in pricing data
-export const getProvider = (
- pricingData: BatchProductPricingResponse | null,
- productSlug: string,
- providerSlug: string
-): ProviderPricingOption | undefined => {
- const product = pricingData?.products?.find(p => p.productSlug === productSlug)
- return product?.providers?.find(p => p.providerSlug === providerSlug)
-}
-
-// Get discounted or original price
-export const getProviderPrice = (
- pricingData: BatchProductPricingResponse | null,
- productSlug: string,
- providerSlug: string
-): number | null => {
- const provider = getProvider(pricingData, productSlug, providerSlug)
- return provider ? (provider.discountedPrice ?? provider.originalPrice) : null
-}
-
-// Get original price
-export const getProviderOriginalPrice = (
- pricingData: BatchProductPricingResponse | null,
- productSlug: string,
- providerSlug: string
-): number | null => {
- return getProvider(pricingData, productSlug, providerSlug)?.originalPrice ?? null
-}
-
-// Get currency code
-export const getProviderCurrencyCode = (
- pricingData: BatchProductPricingResponse | null,
- productSlug: string,
- providerSlug: string
-): string => {
- return getProvider(pricingData, productSlug, providerSlug)?.currencyCode ?? 'EUR'
-}
-
-// Get discount percentage
-export const getDiscountPercent = (
- pricingData: BatchProductPricingResponse | null,
- productSlug: string
-): number | null => {
- return pricingData?.products?.find(p => p.productSlug === productSlug)?.discountPercent ?? null
-}
-
-// Premium tier to product ID mapping
-const TIER_PRODUCT_MAP: Record = {
- [PremiumTier.PREMIUM]: 'premium',
- [PremiumTier.PREMIUM_PLUS]: 'premium_plus',
- [PremiumTier.STARTER]: 'starter_premium'
-}
-
-// Get product ID from premium tier
-export const getTierProductId = (tier: PremiumTier): string => {
- return TIER_PRODUCT_MAP[tier] ?? 'premium'
-}
-
-// Premium tier to subscription product slug mapping
-const TIER_SLUG_MAP: Record = {
- [PremiumTier.PREMIUM]: { monthly: 'l_premium', yearly: 'l_premium-year' },
- [PremiumTier.PREMIUM_PLUS]: { monthly: 'l_prem_plus', yearly: 'l_prem_plus-year' },
- [PremiumTier.STARTER]: { monthly: 'l_starter_premium', yearly: 'l_starter_premium' }
-}
-
-// Get subscription product slug for a tier
-export const getTierSubscriptionSlug = (tier: PremiumTier, isYearly: boolean): string => {
- const slugs = TIER_SLUG_MAP[tier]
- return isYearly ? slugs.yearly : slugs.monthly
-}
-
-// Premium tier to API product ID mapping
-const TIER_API_PRODUCT_MAP: Record = {
- [PremiumTier.PREMIUM]: 'l_premium',
- [PremiumTier.PREMIUM_PLUS]: 'l_prem_plus',
- [PremiumTier.STARTER]: 'l_starter_premium'
-}
-
-// Get API product ID for a tier - supports both legacy boolean and new Duration enum
-export const getTierApiProductId = (tier: PremiumTier, isYearlyOrDuration: boolean | Duration = false): string => {
- const baseId = TIER_API_PRODUCT_MAP[tier]
-
- // Handle Duration enum
- if (typeof isYearlyOrDuration === 'string') {
- switch (isYearlyOrDuration) {
- case Duration.YEARLY:
- return tier !== PremiumTier.STARTER ? `${baseId}-year` : baseId
- case Duration.QUARTER:
- return tier !== PremiumTier.STARTER ? `${baseId}-quarter` : baseId
- case Duration.MONTHLY:
- default:
- return baseId
- }
- }
-
- // Handle legacy boolean
- const isYearly = isYearlyOrDuration as boolean
- return isYearly && tier !== PremiumTier.STARTER ? `${baseId}-year` : baseId
-}
-
-// Fallback subscription prices
-export const SUBSCRIPTION_PRICES: Record = {
- 'premium': { monthly: 9.69, quarterly: 27.69, yearly: 96.69 },
- 'premium_plus': { monthly: 35.69, quarterly: 99.69, yearly: 354.2 },
- 'starter_premium': { monthly: 16.99, quarterly: 16.99, yearly: 16.99 }
-}
-
-// Get fallback subscription price
-export const getFallbackSubscriptionPrice = (productId: string, isYearly: boolean | Duration = false): number => {
- const prices = SUBSCRIPTION_PRICES[productId]
- if (!prices) return -1
-
- // Handle Duration enum
- if (typeof isYearly === 'string') {
- switch (isYearly) {
- case Duration.YEARLY:
- return prices.yearly
- case Duration.QUARTER:
- return prices.quarterly
- case Duration.MONTHLY:
- default:
- return prices.monthly
- }
- }
-
- // Handle legacy boolean
- return (isYearly as boolean) ? prices.yearly : prices.monthly
-}