diff --git a/frontend/src/components/common/SubscriptionProgressMini.vue b/frontend/src/components/common/SubscriptionProgressMini.vue index c915b1dffa..bc55c16411 100644 --- a/frontend/src/components/common/SubscriptionProgressMini.vue +++ b/frontend/src/components/common/SubscriptionProgressMini.vue @@ -184,6 +184,7 @@ import Icon from '@/components/icons/Icon.vue' import { useSubscriptionStore } from '@/stores' import { FeatureFlags, isFeatureFlagEnabled } from '@/utils/featureFlags' import type { UserSubscription } from '@/types' +import { getExpirationDateRelation } from '@/utils/subscriptionQuota' const { t } = useI18n() @@ -264,10 +265,11 @@ function formatDaysRemaining(expiresAt: string): string { const now = new Date() const expires = new Date(expiresAt) const diff = expires.getTime() - now.getTime() - if (diff < 0) return t('subscriptionProgress.expired') + const relation = getExpirationDateRelation(expires, now) + if (relation === 'expired') return t('subscriptionProgress.expired') + if (relation === 'today') return t('subscriptionProgress.expiresToday') + if (relation === 'tomorrow') return t('subscriptionProgress.expiresTomorrow') const days = Math.ceil(diff / (1000 * 60 * 60 * 24)) - if (days === 0) return t('subscriptionProgress.expiresToday') - if (days === 1) return t('subscriptionProgress.expiresTomorrow') return t('subscriptionProgress.daysRemaining', { days }) } diff --git a/frontend/src/components/common/__tests__/SubscriptionProgressMini.spec.ts b/frontend/src/components/common/__tests__/SubscriptionProgressMini.spec.ts new file mode 100644 index 0000000000..cf52174c2f --- /dev/null +++ b/frontend/src/components/common/__tests__/SubscriptionProgressMini.spec.ts @@ -0,0 +1,23 @@ +import { enableAutoUnmount, mount } from '@vue/test-utils' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import SubscriptionProgressMini from '../SubscriptionProgressMini.vue' +const store = vi.hoisted(() => ({ activeSubscriptions: [] as unknown[], hasActiveSubscriptions: true, fetchActiveSubscriptions: vi.fn().mockResolvedValue(undefined) })) +vi.mock('@/stores', () => ({ useSubscriptionStore: () => store })) +vi.mock('@/utils/featureFlags', () => ({ FeatureFlags: { subscription: 'subscription' }, isFeatureFlagEnabled: () => true })) +vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: (key: string) => key }) })) +enableAutoUnmount(afterEach) +beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 8, 22, 12)) }) +afterEach(() => vi.useRealTimers()) +describe('subscription expiry calendar labels', () => { + it.each([ + [new Date(2026, 8, 22, 18), 'expiresToday'], + [new Date(2026, 8, 23, 18), 'expiresTomorrow'], + [new Date(2026, 8, 22, 12), 'expired'], + [new Date(2026, 8, 25, 12), 'daysRemaining'], + ])('labels %s as %s', async (expires, label) => { + store.activeSubscriptions = [{ id: 1, group_id: 1, expires_at: expires.toISOString(), group: { name: 'Plan' } }] + const w = mount(SubscriptionProgressMini, { global: { stubs: { Icon: true, RouterLink: true } } }) + await w.get('button').trigger('click') + expect(w.text()).toContain('subscriptionProgress.' + label) + }) +})