diff --git a/frontend/src/views/admin/SubscriptionsView.vue b/frontend/src/views/admin/SubscriptionsView.vue
index 0a76809e..6c53064d 100644
--- a/frontend/src/views/admin/SubscriptionsView.vue
+++ b/frontend/src/views/admin/SubscriptionsView.vue
@@ -246,7 +246,7 @@
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
/>
- {{ formatResetTime(row.daily_window_start, 'daily') }}
+ {{ formatDailyUsageWindow(row) }}
@@ -758,6 +758,7 @@ 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'
const { t } = useI18n()
const appStore = useAppStore()
@@ -1313,8 +1314,41 @@ const getProgressClass = (used: number | null | undefined, limit: number | null)
return 'bg-green-500'
}
+const formatResetDuration = (parts: RemainingDurationParts): string => {
+ if (parts.days > 0) {
+ return t('admin.subscriptions.resetInDaysHours', { days: parts.days, hours: parts.hours })
+ }
+
+ if (parts.hours > 0) {
+ return t('admin.subscriptions.resetInHoursMinutes', { hours: parts.hours, minutes: parts.minutes })
+ }
+
+ return t('admin.subscriptions.resetInMinutes', { minutes: parts.minutes })
+}
+
+const formatQuotaEndDuration = (parts: RemainingDurationParts): string => {
+ if (parts.days > 0) {
+ return t('admin.subscriptions.quotaEndsInDaysHours', { days: parts.days, hours: parts.hours })
+ }
+
+ if (parts.hours > 0) {
+ return t('admin.subscriptions.quotaEndsInHoursMinutes', { hours: parts.hours, minutes: parts.minutes })
+ }
+
+ return t('admin.subscriptions.quotaEndsInMinutes', { minutes: parts.minutes })
+}
+
+const formatDailyUsageWindow = (subscription: UserSubscription): string => {
+ if (isOneTimeDailyQuota(subscription) && subscription.expires_at) {
+ const parts = getRemainingDurationParts(subscription.expires_at)
+ return parts ? formatQuotaEndDuration(parts) : t('admin.subscriptions.windowNotActive')
+ }
+
+ return formatResetTime(subscription.daily_window_start, 'daily')
+}
+
// Format reset time based on window start and period type
-const formatResetTime = (windowStart: string, period: 'daily' | 'weekly' | 'monthly'): string => {
+const formatResetTime = (windowStart: string | null, period: 'daily' | 'weekly' | 'monthly'): string => {
if (!windowStart) return t('admin.subscriptions.windowNotActive')
const start = new Date(windowStart)
@@ -1334,21 +1368,9 @@ const formatResetTime = (windowStart: string, period: 'daily' | 'weekly' | 'mont
break
}
- const diffMs = resetTime.getTime() - now.getTime()
- if (diffMs <= 0) return t('admin.subscriptions.windowNotActive')
+ const parts = getRemainingDurationParts(resetTime, now)
- const diffSeconds = Math.floor(diffMs / 1000)
- const days = Math.floor(diffSeconds / 86400)
- const hours = Math.floor((diffSeconds % 86400) / 3600)
- const minutes = Math.floor((diffSeconds % 3600) / 60)
-
- if (days > 0) {
- return t('admin.subscriptions.resetInDaysHours', { days, hours })
- } else if (hours > 0) {
- return t('admin.subscriptions.resetInHoursMinutes', { hours, minutes })
- } else {
- return t('admin.subscriptions.resetInMinutes', { minutes })
- }
+ return parts ? formatResetDuration(parts) : t('admin.subscriptions.windowNotActive')
}
// Handle click outside to close dropdowns