Merge pull request #5078 from wucm667/fix/issue-5072-subscription-expiry-label

fix(frontend): correct subscription expiry labels
This commit is contained in:
Wesley Liddick
2026-07-31 11:44:07 +08:00
committed by GitHub
6 changed files with 161 additions and 11 deletions
+29 -4
View File
@@ -353,9 +353,14 @@
>
{{ formatDateTimeToMinute(value) }}
</span>
<div v-if="getDaysRemaining(value) !== null" class="text-xs text-gray-500">
{{ getDaysRemaining(value) }} {{ t('admin.subscriptions.daysRemaining') }}
</div>
<template
v-for="remainingExpiry in [formatRemainingExpiry(value)]"
:key="remainingExpiry ?? 'expired'"
>
<div v-if="remainingExpiry" class="text-xs text-gray-500">
{{ remainingExpiry }}
</div>
</template>
</div>
<span v-else class="text-sm text-gray-500">{{
t('admin.subscriptions.noExpiration')
@@ -777,7 +782,12 @@ import Select from '@/components/common/Select.vue'
import GroupBadge from '@/components/common/GroupBadge.vue'
import GroupOptionItem from '@/components/common/GroupOptionItem.vue'
import Icon from '@/components/icons/Icon.vue'
import { getRemainingDurationParts, isOneTimeDailyQuota, type RemainingDurationParts } from '@/utils/subscriptionQuota'
import {
getRemainingDurationParts,
getRemainingExpiryDuration,
isOneTimeDailyQuota,
type RemainingDurationParts
} from '@/utils/subscriptionQuota'
const { t } = useI18n()
const appStore = useAppStore()
@@ -1334,6 +1344,21 @@ const getDaysRemaining = (expiresAt: string): number | null => {
return Math.ceil(diff / (1000 * 60 * 60 * 24))
}
const formatRemainingExpiry = (expiresAt: string): string | null => {
const duration = getRemainingExpiryDuration(expiresAt)
if (!duration) return null
if (duration.unit === 'days') {
return t('admin.subscriptions.daysRemaining', { days: duration.days })
}
if (duration.hours) {
return t('admin.subscriptions.hoursMinutesRemaining', {
hours: duration.hours,
minutes: duration.minutes
})
}
return t('admin.subscriptions.minutesRemaining', { minutes: duration.minutes })
}
const isExpiringSoon = (expiresAt: string): boolean => {
const days = getDaysRemaining(expiresAt)
return days !== null && days <= 7
+13 -5
View File
@@ -259,7 +259,12 @@ import Icon from '@/components/icons/Icon.vue'
import { formatDateTimeToMinute } from '@/utils/format'
import { hasPeakRate, formatPeakRateWindow, serverTimezoneLabel } from '@/utils/peak-rate'
import { platformBorderClass, platformBadgeClass, platformButtonClass, platformLabel } from '@/utils/platformColors'
import { getRemainingDurationParts, isOneTimeDailyQuota, type RemainingDurationParts } from '@/utils/subscriptionQuota'
import {
getExpirationDateRelation,
getRemainingDurationParts,
isOneTimeDailyQuota,
type RemainingDurationParts
} from '@/utils/subscriptionQuota'
function platformAccentDotClass(p: string): string {
switch (p) {
@@ -317,17 +322,20 @@ function formatExpirationDate(expiresAt: string): string {
const expires = new Date(expiresAt)
const diff = expires.getTime() - now.getTime()
const days = Math.ceil(diff / (1000 * 60 * 60 * 24))
const relation = getExpirationDateRelation(expires, now)
if (days < 0) {
if (relation === null) return ''
if (relation === 'expired') {
return t('userSubscriptions.status.expired')
}
const dateStr = formatDateTimeToMinute(expires)
if (days === 0) {
if (relation === 'today') {
return `${dateStr} (${t('common.today')})`
}
if (days === 1) {
if (relation === 'tomorrow') {
return `${dateStr} (${t('common.tomorrow')})`
}
@@ -340,7 +348,7 @@ function getExpirationClass(expiresAt: string): string {
const diff = expires.getTime() - now.getTime()
const days = Math.ceil(diff / (1000 * 60 * 60 * 24))
if (days <= 0) return 'text-red-600 dark:text-red-400 font-medium'
if (diff <= 0) return 'text-red-600 dark:text-red-400 font-medium'
if (days <= 3) return 'text-red-600 dark:text-red-400'
if (days <= 7) return 'text-orange-600 dark:text-orange-400'
return 'text-gray-700 dark:text-gray-300'