Enhance PaidUntilBadge component to support pending payment review status
CI / Backend (build + test) (push) Successful in 1m20s
CI / Frontend (lint + typecheck + build) (push) Successful in 33s

- Updated the PaidUntilBadge component to accept a new `pendingReview` prop, indicating if a payment is awaiting admin confirmation.
- Modified the rendering logic to display a warning badge when a payment is under review, providing users with a neutral status instead of a negative one.
- Updated relevant components in billing and dashboard routes to pass the new `pendingReview` prop based on the active payment request status.
- Added internationalization support for new labels related to the pending review status in both English and Russian.
This commit is contained in:
Leonid Pershin
2026-07-19 05:35:28 +03:00
parent 24cee9bb78
commit 1c13695556
4 changed files with 33 additions and 4 deletions
@@ -6,10 +6,27 @@ const RED_THRESHOLD_DAYS = 3
const YELLOW_THRESHOLD_DAYS = 7
/** Цветовая индикация оплаченного периода: зелёный — обычный запас, жёлтый — ≤7 дней, красный —
* ≤3 дней или уже истекло. Общий компонент для дашборда пользователя и списка пользователей в админке. */
export function PaidUntilBadge({ paidUntil }: { paidUntil: string | null }) {
* ≤3 дней или уже истекло. Общий компонент для дашборда пользователя и списка пользователей в админке.
*
* `pendingReview` — есть заявка на оплату, ожидающая решения админа (`PaymentRequestStatus.AwaitingConfirmation`):
* конфиги не гасятся, пока админ не решит (см. BillingService), поэтому вместо тревожного "Истекло"
* показываем нейтральный статус — пользователю незачем переживать за то, что от него не зависит. */
export function PaidUntilBadge({
paidUntil,
pendingReview,
}: {
paidUntil: string | null
pendingReview?: boolean
}) {
const { t } = useTranslation()
if (pendingReview)
return (
<Badge variant="warning" title={t('billing.pendingReviewHint')}>
{t('billing.pendingReviewShort')}
</Badge>
)
if (!paidUntil) return <Badge variant="destructive">{t('billing.neverPaidShort')}</Badge>
const days = getDaysRemaining(paidUntil)
+6 -1
View File
@@ -63,7 +63,12 @@ function BillingContent() {
? t('billing.paidUntil', { date: new Date(data.paidUntil).toLocaleDateString() })
: t('billing.neverPaid')}
</p>
{data.paidUntil && <PaidUntilBadge paidUntil={data.paidUntil} />}
{data.paidUntil && (
<PaidUntilBadge
paidUntil={data.paidUntil}
pendingReview={data.activeRequest?.status === 'AwaitingConfirmation'}
/>
)}
</CardContent>
</Card>
+4 -1
View File
@@ -45,7 +45,10 @@ function ConfigsList() {
{billingStatusQuery.data?.billingEnabled && (
<Link to="/billing" className="mt-1 inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground">
{t('billing.paidUntilLabel')}
<PaidUntilBadge paidUntil={billingStatusQuery.data.paidUntil} />
<PaidUntilBadge
paidUntil={billingStatusQuery.data.paidUntil}
pendingReview={billingStatusQuery.data.activeRequest?.status === 'AwaitingConfirmation'}
/>
</Link>
)}
</div>
+4
View File
@@ -114,6 +114,8 @@ const resources = {
daysRemaining: '{{count}} дн.',
hoursMinutesRemaining: '{{hours}} ч {{minutes}} мин',
minutesRemaining: '{{count}} мин',
pendingReviewShort: 'На проверке',
pendingReviewHint: 'Оплата отправлена на проверку администратору — доступ не будет приостановлен, пока заявка не решена.',
newRequest: 'Оформить оплату',
period: 'Период',
periods: {
@@ -638,6 +640,8 @@ const resources = {
daysRemaining: '{{count}}d',
hoursMinutesRemaining: '{{hours}}h {{minutes}}m',
minutesRemaining: '{{count}}m',
pendingReviewShort: 'Under review',
pendingReviewHint: 'Payment sent for admin review — access will not be suspended until the request is decided.',
newRequest: 'Set up payment',
period: 'Period',
periods: {