From 6914e6277bc208f9ffec4bb1691694dca4c8e2b4 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Sun, 19 Apr 2026 20:14:07 +0800 Subject: [PATCH] feat: implement backend routing, dashboard components, and transaction reconciliation scripts --- frontend/components/account/AccountCenter.tsx | 9 +++++-- frontend/components/dashboard/HeaderBar.tsx | 5 ++++ frontend/components/ops/OpsDashboard.tsx | 17 ++++++++++-- scripts/reconcile_payment_tx.py | 6 ++++- web/routes.py | 26 ++++++++++++++++++- 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/frontend/components/account/AccountCenter.tsx b/frontend/components/account/AccountCenter.tsx index fd2a0dfd..3a380913 100644 --- a/frontend/components/account/AccountCenter.tsx +++ b/frontend/components/account/AccountCenter.tsx @@ -1501,8 +1501,13 @@ export function AccountCenter() { ? expiryFormatted : displayExpiryRaw || copy.proPendingSync : copy.noProSubscription; - const showExpiringSoon = - Boolean(isSubscribed && expiryInfo && !expiryInfo.expired && expiryInfo.daysLeft <= 3); + const showExpiringSoon = Boolean( + isSubscribed && + !hasQueuedExtension && + expiryInfo && + !expiryInfo.expired && + expiryInfo.daysLeft <= 3, + ); const showExpiredReminder = Boolean(!isSubscribed && expiryInfo && expiryInfo.expired); const paymentFeatureReady = paymentReadyForRecovery; const canOpenCheckoutOverlay = Boolean( diff --git a/frontend/components/dashboard/HeaderBar.tsx b/frontend/components/dashboard/HeaderBar.tsx index 9e3e92d3..60e34c8d 100644 --- a/frontend/components/dashboard/HeaderBar.tsx +++ b/frontend/components/dashboard/HeaderBar.tsx @@ -47,12 +47,17 @@ export function HeaderBar() { store.proAccess.subscriptionExpiresAt : store.proAccess.subscriptionExpiresAt; const expiryInfo = parseExpiryInfo(effectiveExpiry); + const hasQueuedExtension = Boolean( + store.proAccess.subscriptionActive && + store.proAccess.subscriptionQueuedDays > 0, + ); const isTrialPlan = /trial/i.test( String(store.proAccess.subscriptionPlanCode || ""), ); const showRenewReminder = isAuthenticated && !store.proAccess.loading && + !hasQueuedExtension && ((store.proAccess.subscriptionActive && expiryInfo && expiryInfo.daysLeft <= 3) || diff --git a/frontend/components/ops/OpsDashboard.tsx b/frontend/components/ops/OpsDashboard.tsx index 30d23432..3910eabd 100644 --- a/frontend/components/ops/OpsDashboard.tsx +++ b/frontend/components/ops/OpsDashboard.tsx @@ -286,7 +286,11 @@ type MembershipEntry = { registered_at?: string | null; plan_code?: string | null; starts_at?: string | null; + current_expires_at?: string | null; + total_expires_at?: string | null; expires_at?: string | null; + queued_days?: number | null; + queued_count?: number | null; }; function formatDateTime(value?: string | null) { @@ -301,6 +305,15 @@ function formatUnixDateTime(value?: number | null) { return formatDateTime(new Date(value * 1000).toISOString()); } +function formatMembershipExpiry(item: MembershipEntry) { + const total = item.total_expires_at || item.expires_at; + const current = item.current_expires_at || item.expires_at; + const queuedDays = Math.max(0, Number(item.queued_days || 0)); + const totalLabel = formatDateTime(total); + if (!queuedDays || !current || current === total) return totalLabel; + return `${totalLabel}(已续 +${queuedDays} 天)`; +} + function formatMetric(value?: number | null, digits = 3) { if (value === null || value === undefined || Number.isNaN(value)) return "-"; return Number(value).toFixed(digits); @@ -1535,7 +1548,7 @@ export function OpsDashboard() {