Enhance PaidUntilBadge and UserManageDialog to display billing date
CI / Backend (build + test) (push) Successful in 1m18s
CI / Frontend (lint + typecheck + build) (push) Successful in 31s

- Updated the PaidUntilBadge component to accept a new `showDate` prop, allowing it to display the billing date alongside the remaining days.
- Modified UserManageDialog and admin user management components to utilize the updated PaidUntilBadge, improving context for billing information.
- Added internationalization support for the new date format in both English and Russian.
This commit is contained in:
Leonid Pershin
2026-07-19 05:57:13 +03:00
parent 1c13695556
commit 6a2d2d2318
4 changed files with 27 additions and 7 deletions
@@ -155,7 +155,7 @@ export function UserManageDialog({ user, open, onOpenChange }: { user: UserSumma
{user.billingEnabled && (
<div className="flex flex-col gap-1.5">
<Label>{t('admin.users.billingLabel')}</Label>
<PaidUntilBadge paidUntil={user.billingPaidUntil} />
<PaidUntilBadge paidUntil={user.billingPaidUntil} showDate />
<div className="mt-1 flex gap-2">
<Input
type="number"
@@ -10,31 +10,49 @@ const YELLOW_THRESHOLD_DAYS = 7
*
* `pendingReview` — есть заявка на оплату, ожидающая решения админа (`PaymentRequestStatus.AwaitingConfirmation`):
* конфиги не гасятся, пока админ не решит (см. BillingService), поэтому вместо тревожного "Истекло"
* показываем нейтральный статус — пользователю незачем переживать за то, что от него не зависит. */
* показываем нейтральный статус — пользователю незачем переживать за то, что от него не зависит.
*
* `showDate` — добавляет саму дату к остатку («до 15.08.2026 (99 дн.)») — для админских экранов
* (карточка пользователя, список пользователей), где голого «99 дн.» недостаточно контекста.
* Компактные пользовательские места (дашборд) оставляют только остаток. */
export function PaidUntilBadge({
paidUntil,
pendingReview,
showDate,
}: {
paidUntil: string | null
pendingReview?: boolean
showDate?: boolean
}) {
const { t } = useTranslation()
if (pendingReview)
return (
<Badge variant="warning" title={t('billing.pendingReviewHint')}>
<Badge variant="warning" className="self-start" title={t('billing.pendingReviewHint')}>
{t('billing.pendingReviewShort')}
</Badge>
)
if (!paidUntil) return <Badge variant="destructive">{t('billing.neverPaidShort')}</Badge>
if (!paidUntil)
return (
<Badge variant="destructive" className="self-start">
{t('billing.neverPaidShort')}
</Badge>
)
const days = getDaysRemaining(paidUntil)
const variant = days <= RED_THRESHOLD_DAYS ? 'destructive' : days <= YELLOW_THRESHOLD_DAYS ? 'warning' : 'success'
const remaining = formatRemaining(paidUntil, t)
const label = showDate
? t('billing.paidUntilWithRemaining', {
date: new Date(paidUntil).toLocaleDateString(),
remaining,
})
: remaining
return (
<Badge variant={variant} title={new Date(paidUntil).toLocaleString()}>
{formatRemaining(paidUntil, t)}
<Badge variant={variant} className="self-start" title={new Date(paidUntil).toLocaleString()}>
{label}
</Badge>
)
}
+1 -1
View File
@@ -77,7 +77,7 @@ function AdminUsersPage() {
</td>
<td className="py-2">
{user.billingEnabled ? (
<PaidUntilBadge paidUntil={user.billingPaidUntil} />
<PaidUntilBadge paidUntil={user.billingPaidUntil} showDate />
) : (
<span className="text-muted-foreground"></span>
)}
+2
View File
@@ -116,6 +116,7 @@ const resources = {
minutesRemaining: '{{count}} мин',
pendingReviewShort: 'На проверке',
pendingReviewHint: 'Оплата отправлена на проверку администратору — доступ не будет приостановлен, пока заявка не решена.',
paidUntilWithRemaining: 'до {{date}} ({{remaining}})',
newRequest: 'Оформить оплату',
period: 'Период',
periods: {
@@ -642,6 +643,7 @@ const resources = {
minutesRemaining: '{{count}}m',
pendingReviewShort: 'Under review',
pendingReviewHint: 'Payment sent for admin review — access will not be suspended until the request is decided.',
paidUntilWithRemaining: 'until {{date}} ({{remaining}})',
newRequest: 'Set up payment',
period: 'Period',
periods: {