fix: fix SubscriptionPage crash — align with API response structure

API returns { subscription: {...}|null, used_trial: bool } not flat MySubscription.
- Added MySubscriptionData interface with correct nested shape
- Extract my = myRaw.subscription and usedTrial = myRaw.used_trial separately
- Replace my.used_trial references with usedTrial variable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-06-14 23:12:22 +03:30
co-authored by Claude Sonnet 4.6
parent 94580894c8
commit ab9d078bf0
2 changed files with 20 additions and 6 deletions
+8 -6
View File
@@ -4,7 +4,7 @@ import { CheckIcon, SparklesIcon } from '@heroicons/react/24/outline';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { api } from '../lib/api'; import { api } from '../lib/api';
import type { ApiResponse } from '../lib/api'; import type { ApiResponse } from '../lib/api';
import type { SubscriptionPlan, MySubscription, SubscriptionPeriod } from '../types'; import type { SubscriptionPlan, MySubscriptionData, SubscriptionPeriod } from '../types';
import { formatDate, formatRial, formatNumber } from '../lib/utils'; import { formatDate, formatRial, formatNumber } from '../lib/utils';
import Modal from '../components/ui/Modal'; import Modal from '../components/ui/Modal';
import PageHeader from '../components/ui/PageHeader'; import PageHeader from '../components/ui/PageHeader';
@@ -33,13 +33,15 @@ export default function SubscriptionPage() {
queryFn: () => api.get('/api/v1/subscription/plans'), queryFn: () => api.get('/api/v1/subscription/plans'),
}); });
const { data: myData } = useQuery<ApiResponse<MySubscription>>({ const { data: myData } = useQuery<ApiResponse<MySubscriptionData>>({
queryKey: ['subscription-my'], queryKey: ['subscription-my'],
queryFn: () => api.get('/api/v1/subscription/my'), queryFn: () => api.get('/api/v1/subscription/my'),
}); });
const plans = plansData?.data ?? []; const plans = plansData?.data ?? [];
const my = myData?.data; const myRaw = myData?.data;
const my = myRaw?.subscription ?? null;
const usedTrial = myRaw?.used_trial ?? false;
const trialMutation = useMutation({ const trialMutation = useMutation({
mutationFn: () => api.post('/api/v1/subscription/trial', {}), mutationFn: () => api.post('/api/v1/subscription/trial', {}),
@@ -89,7 +91,7 @@ export default function SubscriptionPage() {
: <div style={{ fontSize: 13, color: 'var(--text-3)' }}>بدون تاریخ انقضا</div> : <div style={{ fontSize: 13, color: 'var(--text-3)' }}>بدون تاریخ انقضا</div>
} }
</div> </div>
{!my.used_trial && ( {!usedTrial && (
<button <button
className="btn primary sm" className="btn primary sm"
style={{ marginRight: 'auto' }} style={{ marginRight: 'auto' }}
@@ -118,7 +120,7 @@ export default function SubscriptionPage() {
key={plan.uuid} key={plan.uuid}
plan={plan} plan={plan}
currentPlanLevel={my?.plan.level ?? 0} currentPlanLevel={my?.plan.level ?? 0}
usedTrial={my?.used_trial ?? false} usedTrial={usedTrial}
onPurchase={(period) => setPurchaseTarget(period)} onPurchase={(period) => setPurchaseTarget(period)}
/> />
))} ))}
+12
View File
@@ -337,6 +337,18 @@ export interface SubscriptionPeriod {
is_trial: boolean; is_trial: boolean;
} }
export interface MySubscriptionData {
subscription: {
plan: { name: string; level: number; features: Record<string, boolean> };
period?: { label: string; duration_months: number };
is_trial: boolean;
starts_at?: number;
expires_at?: number | null;
days_remaining?: number;
} | null;
used_trial: boolean;
}
/** @deprecated use MySubscriptionData */
export interface MySubscription { export interface MySubscription {
plan: { name: string; level: number; features: Record<string, boolean> }; plan: { name: string; level: number; features: Record<string, boolean> };
period?: { label: string; duration_months: number }; period?: { label: string; duration_months: number };