Enhance PaidUntilBadge and UserManageDialog to display billing date
- 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:
@@ -155,7 +155,7 @@ export function UserManageDialog({ user, open, onOpenChange }: { user: UserSumma
|
|||||||
{user.billingEnabled && (
|
{user.billingEnabled && (
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<Label>{t('admin.users.billingLabel')}</Label>
|
<Label>{t('admin.users.billingLabel')}</Label>
|
||||||
<PaidUntilBadge paidUntil={user.billingPaidUntil} />
|
<PaidUntilBadge paidUntil={user.billingPaidUntil} showDate />
|
||||||
<div className="mt-1 flex gap-2">
|
<div className="mt-1 flex gap-2">
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
|
|||||||
@@ -10,31 +10,49 @@ const YELLOW_THRESHOLD_DAYS = 7
|
|||||||
*
|
*
|
||||||
* `pendingReview` — есть заявка на оплату, ожидающая решения админа (`PaymentRequestStatus.AwaitingConfirmation`):
|
* `pendingReview` — есть заявка на оплату, ожидающая решения админа (`PaymentRequestStatus.AwaitingConfirmation`):
|
||||||
* конфиги не гасятся, пока админ не решит (см. BillingService), поэтому вместо тревожного "Истекло"
|
* конфиги не гасятся, пока админ не решит (см. BillingService), поэтому вместо тревожного "Истекло"
|
||||||
* показываем нейтральный статус — пользователю незачем переживать за то, что от него не зависит. */
|
* показываем нейтральный статус — пользователю незачем переживать за то, что от него не зависит.
|
||||||
|
*
|
||||||
|
* `showDate` — добавляет саму дату к остатку («до 15.08.2026 (99 дн.)») — для админских экранов
|
||||||
|
* (карточка пользователя, список пользователей), где голого «99 дн.» недостаточно контекста.
|
||||||
|
* Компактные пользовательские места (дашборд) оставляют только остаток. */
|
||||||
export function PaidUntilBadge({
|
export function PaidUntilBadge({
|
||||||
paidUntil,
|
paidUntil,
|
||||||
pendingReview,
|
pendingReview,
|
||||||
|
showDate,
|
||||||
}: {
|
}: {
|
||||||
paidUntil: string | null
|
paidUntil: string | null
|
||||||
pendingReview?: boolean
|
pendingReview?: boolean
|
||||||
|
showDate?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
if (pendingReview)
|
if (pendingReview)
|
||||||
return (
|
return (
|
||||||
<Badge variant="warning" title={t('billing.pendingReviewHint')}>
|
<Badge variant="warning" className="self-start" title={t('billing.pendingReviewHint')}>
|
||||||
{t('billing.pendingReviewShort')}
|
{t('billing.pendingReviewShort')}
|
||||||
</Badge>
|
</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 days = getDaysRemaining(paidUntil)
|
||||||
const variant = days <= RED_THRESHOLD_DAYS ? 'destructive' : days <= YELLOW_THRESHOLD_DAYS ? 'warning' : 'success'
|
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 (
|
return (
|
||||||
<Badge variant={variant} title={new Date(paidUntil).toLocaleString()}>
|
<Badge variant={variant} className="self-start" title={new Date(paidUntil).toLocaleString()}>
|
||||||
{formatRemaining(paidUntil, t)}
|
{label}
|
||||||
</Badge>
|
</Badge>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ function AdminUsersPage() {
|
|||||||
</td>
|
</td>
|
||||||
<td className="py-2">
|
<td className="py-2">
|
||||||
{user.billingEnabled ? (
|
{user.billingEnabled ? (
|
||||||
<PaidUntilBadge paidUntil={user.billingPaidUntil} />
|
<PaidUntilBadge paidUntil={user.billingPaidUntil} showDate />
|
||||||
) : (
|
) : (
|
||||||
<span className="text-muted-foreground">—</span>
|
<span className="text-muted-foreground">—</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ const resources = {
|
|||||||
minutesRemaining: '{{count}} мин',
|
minutesRemaining: '{{count}} мин',
|
||||||
pendingReviewShort: 'На проверке',
|
pendingReviewShort: 'На проверке',
|
||||||
pendingReviewHint: 'Оплата отправлена на проверку администратору — доступ не будет приостановлен, пока заявка не решена.',
|
pendingReviewHint: 'Оплата отправлена на проверку администратору — доступ не будет приостановлен, пока заявка не решена.',
|
||||||
|
paidUntilWithRemaining: 'до {{date}} ({{remaining}})',
|
||||||
newRequest: 'Оформить оплату',
|
newRequest: 'Оформить оплату',
|
||||||
period: 'Период',
|
period: 'Период',
|
||||||
periods: {
|
periods: {
|
||||||
@@ -642,6 +643,7 @@ const resources = {
|
|||||||
minutesRemaining: '{{count}}m',
|
minutesRemaining: '{{count}}m',
|
||||||
pendingReviewShort: 'Under review',
|
pendingReviewShort: 'Under review',
|
||||||
pendingReviewHint: 'Payment sent for admin review — access will not be suspended until the request is decided.',
|
pendingReviewHint: 'Payment sent for admin review — access will not be suspended until the request is decided.',
|
||||||
|
paidUntilWithRemaining: 'until {{date}} ({{remaining}})',
|
||||||
newRequest: 'Set up payment',
|
newRequest: 'Set up payment',
|
||||||
period: 'Period',
|
period: 'Period',
|
||||||
periods: {
|
periods: {
|
||||||
|
|||||||
Reference in New Issue
Block a user