From f1d58e16044af54f87f477829752ea5aa65afe69 Mon Sep 17 00:00:00 2001
From: hamed <15238-genius.ha@users.noreply.drupalcode.org>
Date: Sat, 18 Jul 2026 11:05:47 +0330
Subject: [PATCH] feat(subscription): display jalali expiry date and handle no
expiry case in current plan card
---
assets/admin/pages/SubscriptionPage.test.tsx | 23 +++++++++++++++++++-
assets/admin/pages/SubscriptionPage.tsx | 20 +++++++++++++++--
2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/assets/admin/pages/SubscriptionPage.test.tsx b/assets/admin/pages/SubscriptionPage.test.tsx
index 5e56849d..b0dd02ce 100644
--- a/assets/admin/pages/SubscriptionPage.test.tsx
+++ b/assets/admin/pages/SubscriptionPage.test.tsx
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { screen, fireEvent, within } from '@testing-library/react';
import { renderWithProviders } from '../test/utils';
-import { formatRial } from '../lib/utils';
+import { formatRial, formatDate } from '../lib/utils';
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }));
vi.mock('../lib/api', () => ({
@@ -67,6 +67,27 @@ describe('SubscriptionPage', () => {
expect(await screen.findByText(/روز تا پایان اشتراک/)).toBeInTheDocument();
});
+ it('shows the jalali expiry date on the current-plan card', async () => {
+ // 1767225600 = 2026-01-01T00:00:00Z → ۱۴۰۴/۱۰/۱۱ in Tehran
+ mockApi({ my: { ...DEFAULT_MY, subscription: { ...DEFAULT_MY.subscription, expires_at: 1767225600 } } });
+ renderWithProviders(, { route: '/admin/subscription' });
+
+ expect(await screen.findByText('زمان پایان اشتراک:')).toBeInTheDocument();
+ expect(screen.getByText(formatDate(1767225600))).toBeInTheDocument();
+ expect(screen.getByText(/روز مانده/)).toBeInTheDocument();
+ });
+
+ it('falls back to «بدون تاریخ انقضا» when the subscription has no expiry', async () => {
+ renderWithProviders(, { route: '/admin/subscription' });
+ expect(await screen.findByText('بدون تاریخ انقضا')).toBeInTheDocument();
+ });
+
+ it('renders the current-plan card right-to-left', async () => {
+ renderWithProviders(, { route: '/admin/subscription' });
+ const label = await screen.findByText('پلن فعلی شما:');
+ expect(label.closest('[dir]')).toHaveAttribute('dir', 'rtl');
+ });
+
it('shows the expired message when no days remain', async () => {
mockApi({ my: { ...DEFAULT_MY, subscription: { ...DEFAULT_MY.subscription, days_remaining: 0 } } });
renderWithProviders(, { route: '/admin/subscription' });
diff --git a/assets/admin/pages/SubscriptionPage.tsx b/assets/admin/pages/SubscriptionPage.tsx
index 4ba11c44..b9ae8104 100644
--- a/assets/admin/pages/SubscriptionPage.tsx
+++ b/assets/admin/pages/SubscriptionPage.tsx
@@ -7,7 +7,7 @@ import { api } from '../lib/api';
import type { ApiResponse } from '../lib/api';
import type { SubscriptionPlan, MySubscriptionData, SubscriptionPeriod } from '../types';
import { usePaymentConfig } from '../hooks/usePaymentConfig';
-import { formatRial, formatNumber } from '../lib/utils';
+import { formatRial, formatNumber, formatDate } from '../lib/utils';
import Modal from '../components/ui/Modal';
import SettingsLayout from '../components/layout/SettingsLayout';
import {
@@ -270,9 +270,10 @@ export default function SubscriptionPage() {
function CurrentPlanCard({ my, onRenew }: { my: MySubscriptionData['subscription']; onRenew: () => void }) {
const daysLeft = my?.days_remaining ?? 0;
+ const expiresAt = my?.expires_at ?? null;
return (
پلن فعلی شما:
{planMetaOf(my.plan.name).label}
+ {expiresAt ? (
+
+ زمان پایان اشتراک:
+ {formatDate(expiresAt)}
+ {daysLeft > 0 && (
+
+ ({formatNumber(daysLeft)} روز مانده)
+
+ )}
+
+ ) : (
+
+ بدون تاریخ انقضا
+
+ )}
{daysLeft < 3 && (