-
+
@@ -138,9 +138,8 @@ import Button from 'primevue/button'
import { computed, onBeforeUnmount, watch } from 'vue'
import CloudBadge from '@/components/topbar/CloudBadge.vue'
-import { useFeatureFlags } from '@/composables/useFeatureFlags'
import { MONTHLY_SUBSCRIPTION_PRICE } from '@/config/subscriptionPricesConfig'
-import StripePricingTable from '@/platform/cloud/subscription/components/StripePricingTable.vue'
+import PricingTable from '@/platform/cloud/subscription/components/PricingTable.vue'
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
import SubscriptionBenefits from '@/platform/cloud/subscription/components/SubscriptionBenefits.vue'
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
@@ -168,25 +167,18 @@ const formattedMonthlyPrice = new Intl.NumberFormat(
maximumFractionDigits: 0
}
).format(MONTHLY_SUBSCRIPTION_PRICE)
-const { featureFlag } = useFeatureFlags()
-const subscriptionTiersEnabled = featureFlag(
- 'subscription_tiers_enabled',
- false
-)
const commandStore = useCommandStore()
const telemetry = useTelemetry()
-const showStripePricingTable = computed(
- () =>
- subscriptionTiersEnabled.value &&
- isCloud &&
- window.__CONFIG__?.subscription_required
+// Always show custom pricing table for cloud subscriptions
+const showCustomPricingTable = computed(
+ () => isCloud && window.__CONFIG__?.subscription_required
)
const POLL_INTERVAL_MS = 3000
-const MAX_POLL_DURATION_MS = 5 * 60 * 1000
+const MAX_POLL_ATTEMPTS = 3
let pollInterval: number | null = null
-let pollStartTime = 0
+let pollAttempts = 0
const stopPolling = () => {
if (pollInterval) {
@@ -197,35 +189,44 @@ const stopPolling = () => {
const startPolling = () => {
stopPolling()
- pollStartTime = Date.now()
+ pollAttempts = 0
const poll = async () => {
try {
await fetchStatus()
+ pollAttempts++
+
+ if (pollAttempts >= MAX_POLL_ATTEMPTS) {
+ stopPolling()
+ }
} catch (error) {
console.error(
'[SubscriptionDialog] Failed to poll subscription status',
error
)
+ stopPolling()
}
}
void poll()
pollInterval = window.setInterval(() => {
- if (Date.now() - pollStartTime > MAX_POLL_DURATION_MS) {
- stopPolling()
- return
- }
void poll()
}, POLL_INTERVAL_MS)
}
+const handleWindowFocus = () => {
+ if (showCustomPricingTable.value) {
+ startPolling()
+ }
+}
+
watch(
- showStripePricingTable,
+ showCustomPricingTable,
(enabled) => {
if (enabled) {
- startPolling()
+ window.addEventListener('focus', handleWindowFocus)
} else {
+ window.removeEventListener('focus', handleWindowFocus)
stopPolling()
}
},
@@ -235,7 +236,7 @@ watch(
watch(
() => isActiveSubscription.value,
(isActive) => {
- if (isActive && showStripePricingTable.value) {
+ if (isActive && showCustomPricingTable.value) {
emit('close', true)
}
}
@@ -270,6 +271,7 @@ const handleViewEnterprise = () => {
onBeforeUnmount(() => {
stopPolling()
+ window.removeEventListener('focus', handleWindowFocus)
})
diff --git a/src/platform/cloud/subscription/composables/useStripePricingTableLoader.ts b/src/platform/cloud/subscription/composables/useStripePricingTableLoader.ts
deleted file mode 100644
index 06776166b..000000000
--- a/src/platform/cloud/subscription/composables/useStripePricingTableLoader.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import { createSharedComposable } from '@vueuse/core'
-import { ref } from 'vue'
-
-import { STRIPE_PRICING_TABLE_SCRIPT_SRC } from '@/config/stripePricingTableConfig'
-
-function useStripePricingTableLoaderInternal() {
- const isLoaded = ref(false)
- const isLoading = ref(false)
- const error = ref(null)
- let pendingPromise: Promise | null = null
-
- const resolveLoaded = () => {
- isLoaded.value = true
- isLoading.value = false
- pendingPromise = null
- }
-
- const resolveError = (err: Error) => {
- error.value = err
- isLoading.value = false
- pendingPromise = null
- }
-
- const loadScript = (): Promise => {
- if (isLoaded.value) {
- return Promise.resolve()
- }
-
- if (pendingPromise) {
- return pendingPromise
- }
-
- const existingScript = document.querySelector(
- `script[src="${STRIPE_PRICING_TABLE_SCRIPT_SRC}"]`
- )
-
- if (existingScript) {
- isLoading.value = true
-
- pendingPromise = new Promise((resolve, reject) => {
- existingScript.addEventListener(
- 'load',
- () => {
- existingScript.dataset.loaded = 'true'
- resolveLoaded()
- resolve()
- },
- { once: true }
- )
- existingScript.addEventListener(
- 'error',
- () => {
- const err = new Error('Stripe pricing table script failed to load')
- resolveError(err)
- reject(err)
- },
- { once: true }
- )
-
- // Check if script already loaded after attaching listeners
- if (
- existingScript.dataset.loaded === 'true' ||
- (existingScript as any).readyState === 'complete' ||
- (existingScript as any).complete
- ) {
- existingScript.dataset.loaded = 'true'
- resolveLoaded()
- resolve()
- }
- })
-
- return pendingPromise
- }
-
- isLoading.value = true
- pendingPromise = new Promise((resolve, reject) => {
- const script = document.createElement('script')
- script.src = STRIPE_PRICING_TABLE_SCRIPT_SRC
- script.async = true
- script.dataset.loaded = 'false'
-
- script.addEventListener(
- 'load',
- () => {
- script.dataset.loaded = 'true'
- resolveLoaded()
- resolve()
- },
- { once: true }
- )
-
- script.addEventListener(
- 'error',
- () => {
- const err = new Error('Stripe pricing table script failed to load')
- resolveError(err)
- reject(err)
- },
- { once: true }
- )
-
- document.head.appendChild(script)
- })
-
- return pendingPromise
- }
-
- return {
- loadScript,
- isLoaded,
- isLoading,
- error
- }
-}
-
-export const useStripePricingTableLoader = createSharedComposable(
- useStripePricingTableLoaderInternal
-)
diff --git a/src/platform/cloud/subscription/composables/useSubscription.ts b/src/platform/cloud/subscription/composables/useSubscription.ts
index 3e9b5950d..c207ad1d6 100644
--- a/src/platform/cloud/subscription/composables/useSubscription.ts
+++ b/src/platform/cloud/subscription/composables/useSubscription.ts
@@ -242,6 +242,7 @@ function useSubscriptionInternal() {
formattedEndDate,
subscriptionTier,
subscriptionTierName,
+ subscriptionStatus,
// Actions
subscribe,
diff --git a/tests-ui/tests/platform/cloud/subscription/components/StripePricingTable.test.ts b/tests-ui/tests/platform/cloud/subscription/components/StripePricingTable.test.ts
deleted file mode 100644
index 9a4d9a352..000000000
--- a/tests-ui/tests/platform/cloud/subscription/components/StripePricingTable.test.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-import { mount, flushPromises } from '@vue/test-utils'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
-import { createI18n } from 'vue-i18n'
-import { ref } from 'vue'
-
-import enMessages from '@/locales/en/main.json' with { type: 'json' }
-import StripePricingTable from '@/platform/cloud/subscription/components/StripePricingTable.vue'
-
-const mockLoadStripeScript = vi.fn()
-let currentConfig = {
- publishableKey: 'pk_test_123',
- pricingTableId: 'prctbl_123'
-}
-let hasConfig = true
-
-vi.mock('@/config/stripePricingTableConfig', () => ({
- getStripePricingTableConfig: () => currentConfig,
- hasStripePricingTableConfig: () => hasConfig
-}))
-
-const mockIsLoaded = ref(false)
-const mockIsLoading = ref(false)
-const mockError = ref(null)
-
-vi.mock(
- '@/platform/cloud/subscription/composables/useStripePricingTableLoader',
- () => ({
- useStripePricingTableLoader: () => ({
- loadScript: mockLoadStripeScript,
- isLoaded: mockIsLoaded,
- isLoading: mockIsLoading,
- error: mockError
- })
- })
-)
-
-const i18n = createI18n({
- legacy: false,
- locale: 'en',
- messages: { en: enMessages }
-})
-
-const mountComponent = () =>
- mount(StripePricingTable, {
- global: {
- plugins: [i18n]
- }
- })
-
-describe('StripePricingTable', () => {
- beforeEach(() => {
- currentConfig = {
- publishableKey: 'pk_test_123',
- pricingTableId: 'prctbl_123'
- }
- hasConfig = true
- mockLoadStripeScript.mockReset().mockResolvedValue(undefined)
- mockIsLoaded.value = false
- mockIsLoading.value = false
- mockError.value = null
- })
-
- it('renders the Stripe pricing table when config is available', async () => {
- const wrapper = mountComponent()
-
- await flushPromises()
-
- expect(mockLoadStripeScript).toHaveBeenCalled()
-
- const stripePricingTable = wrapper.find('stripe-pricing-table')
- expect(stripePricingTable.exists()).toBe(true)
- expect(stripePricingTable.attributes('publishable-key')).toBe('pk_test_123')
- expect(stripePricingTable.attributes('pricing-table-id')).toBe('prctbl_123')
- })
-
- it('shows missing config message when credentials are absent', () => {
- hasConfig = false
- currentConfig = { publishableKey: '', pricingTableId: '' }
-
- const wrapper = mountComponent()
-
- expect(
- wrapper.find('[data-testid="stripe-table-missing-config"]').exists()
- ).toBe(true)
- expect(mockLoadStripeScript).not.toHaveBeenCalled()
- })
-
- it('shows loading indicator when script is loading', async () => {
- // Mock loadScript to never resolve, simulating loading state
- mockLoadStripeScript.mockImplementation(() => new Promise(() => {}))
-
- const wrapper = mountComponent()
- await flushPromises()
-
- expect(wrapper.find('[data-testid="stripe-table-loading"]').exists()).toBe(
- true
- )
- expect(wrapper.find('stripe-pricing-table').exists()).toBe(false)
- })
-
- it('shows error indicator when script fails to load', async () => {
- // Mock loadScript to reject, simulating error state
- mockLoadStripeScript.mockRejectedValue(new Error('Script failed to load'))
-
- const wrapper = mountComponent()
- await flushPromises()
-
- expect(wrapper.find('[data-testid="stripe-table-error"]').exists()).toBe(
- true
- )
- expect(wrapper.find('stripe-pricing-table').exists()).toBe(false)
- })
-})