diff --git a/src/components/features/subscription/LimitReachedModal.tsx b/src/components/features/subscription/LimitReachedModal.tsx index 616e2e324..5baa331ae 100644 --- a/src/components/features/subscription/LimitReachedModal.tsx +++ b/src/components/features/subscription/LimitReachedModal.tsx @@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { AlertTriangle, Zap, Clock } from 'lucide-react'; +import { useSubscription } from '@/hooks/use-subscription'; interface LimitReachedModalProps { open: boolean; @@ -14,7 +15,10 @@ interface LimitReachedModalProps { export function LimitReachedModal({ open, onOpenChange, variant, limit, current, onUpgrade }: LimitReachedModalProps) { const { t } = useTranslation(); + const { isPro } = useSubscription(); const isTrialExpired = variant === 'trial_expired'; + // A Pro student who used today's included requests already pays. Selling Pro again is the wrong next step. + const paidDailyCap = isPro && !isTrialExpired; return ( @@ -38,16 +42,24 @@ export function LimitReachedModal({ open, onOpenChange, variant, limit, current, - - - {t('limitModal.upgrade')} - - onOpenChange(false)}> - {t('limitModal.notNow', 'Not now')} - - - {t('limitModal.upgradeNote')} - + {paidDailyCap ? ( + onOpenChange(false)} size="lg" className="w-full"> + {t('limitModal.gotIt')} + + ) : ( + <> + + + {t('limitModal.upgrade')} + + onOpenChange(false)}> + {t('limitModal.notNow', 'Not now')} + + + {t('limitModal.upgradeNote')} + + > + )} diff --git a/src/locales/en.json b/src/locales/en.json index 7a2b4ac96..422e4d7bf 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1332,7 +1332,8 @@ "limitReachedDesc": "You've used {{current}} of {{limit}} included AI uses today. Access refreshes at midnight UTC.", "upgrade": "Upgrade to Pro - $5/mo", "upgradeNote": "More AI prep access + high reasoning", - "notNow": "Not now" + "notNow": "Not now", + "gotIt": "OK" }, "modelModal": { "title": "Pro Reasoning", diff --git a/src/locales/es.json b/src/locales/es.json index e03254f8e..d5befc9db 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1332,7 +1332,8 @@ "limitReachedDesc": "Has usado {{current}} de {{limit}} usos de IA incluidos hoy. El acceso se renueva a medianoche UTC.", "upgrade": "Actualizar a Pro - $5/mes", "upgradeNote": "Más acceso a preparación con IA + razonamiento alto", - "notNow": "Ahora no" + "notNow": "Ahora no", + "gotIt": "Entendido" }, "modelModal": { "title": "Razonamiento Pro", diff --git a/src/locales/hu.json b/src/locales/hu.json index 46a0e4953..ef26e69c2 100644 --- a/src/locales/hu.json +++ b/src/locales/hu.json @@ -1332,7 +1332,8 @@ "limitReachedDesc": "Ma {{current}} / {{limit}} benne foglalt MI használatot használtál fel. A hozzáférés éjfélkor (UTC) frissül.", "upgrade": "Frissítés Pro-ra - $5/hó", "upgradeNote": "Több MI felkészülési hozzáférés + magas szintű érvelés", - "notNow": "Most nem" + "notNow": "Most nem", + "gotIt": "Rendben" }, "modelModal": { "title": "Pro érvelés", diff --git a/src/test/components/LimitReachedModal.test.tsx b/src/test/components/LimitReachedModal.test.tsx new file mode 100644 index 000000000..dca1c5331 --- /dev/null +++ b/src/test/components/LimitReachedModal.test.tsx @@ -0,0 +1,52 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { LimitReachedModal } from '@/components/features/subscription/LimitReachedModal'; + +const subscription = vi.hoisted(() => ({ isPro: false })); + +vi.mock('@/hooks/use-subscription', () => ({ + useSubscription: () => ({ isPro: subscription.isPro, loading: false, tier: subscription.isPro ? 'pro' : 'trial' }), +})); + +function renderModal(variant: 'limit_reached' | 'trial_expired' = 'limit_reached') { + const onUpgrade = vi.fn(); + const onOpenChange = vi.fn(); + render( + , + ); + return { onUpgrade, onOpenChange }; +} + +describe('LimitReachedModal', () => { + beforeEach(() => { + subscription.isPro = false; + }); + + it('offers Pro when a free plan hits the daily cap', () => { + renderModal(); + expect(screen.getByRole('button', { name: 'Upgrade to Pro - $5/mo' })).toBeInTheDocument(); + }); + + it('does not sell Pro again when a paying student hits the daily cap', async () => { + subscription.isPro = true; + const { onUpgrade, onOpenChange } = renderModal(); + expect(screen.queryByRole('button', { name: 'Upgrade to Pro - $5/mo' })).not.toBeInTheDocument(); + expect(screen.queryByText('More AI prep access + high reasoning')).not.toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: 'OK' })); + expect(onOpenChange).toHaveBeenCalledWith(false); + expect(onUpgrade).not.toHaveBeenCalled(); + }); + + it('still offers Pro when the trial has ended', () => { + subscription.isPro = false; + renderModal('trial_expired'); + expect(screen.getByRole('button', { name: 'Upgrade to Pro - $5/mo' })).toBeInTheDocument(); + }); +});
- {t('limitModal.upgradeNote')} -
+ {t('limitModal.upgradeNote')} +