Fixed empty "Resets on" date display for new users: Issue: - Users who haven't created any conversations yet have limitResetDate = NULL - The "Resets on" field was showing empty/blank - This confused users about when their limit would reset Solution: - Updated formatResetDate() in 3 components to calculate default date - If limitResetDate is NULL, display "1 month from now" - This gives users a clear expectation of when limits reset Files Updated: - app/[locale]/subscription/page.tsx * formatResetDate() now returns calculated date if null - components/subscription/usage-display.tsx * formatResetDate() now returns calculated date if null - components/subscription/upgrade-modal.tsx * formatResetDate() now returns calculated date if null * Removed conditional check - always show reset date User Experience: - New users see "Resets on: [date one month from now]" - Once they create their first conversation, actual reset date is set - Consistent messaging across all subscription UI components Note: The actual limitResetDate is set when the user creates their first conversation (in incrementConversationCount function). This fix only affects the UI display for users who haven't chatted yet. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
220 lines
6.2 KiB
TypeScript
220 lines
6.2 KiB
TypeScript
'use client'
|
|
|
|
import {
|
|
Dialog,
|
|
DialogTitle,
|
|
DialogContent,
|
|
DialogActions,
|
|
Box,
|
|
Typography,
|
|
Button,
|
|
LinearProgress,
|
|
Chip
|
|
} from '@mui/material'
|
|
import {
|
|
Favorite,
|
|
AutoAwesome,
|
|
Close as CloseIcon
|
|
} from '@mui/icons-material'
|
|
import { useTranslations, useLocale } from 'next-intl'
|
|
import Link from 'next/link'
|
|
|
|
interface UpgradeModalProps {
|
|
open: boolean
|
|
onClose: () => void
|
|
limitData?: {
|
|
limit: number
|
|
remaining: number
|
|
tier: string
|
|
resetDate: string | null
|
|
}
|
|
}
|
|
|
|
export default function UpgradeModal({ open, onClose, limitData }: UpgradeModalProps) {
|
|
const locale = useLocale()
|
|
const t = useTranslations('subscription.limitReached')
|
|
|
|
const usagePercentage = limitData ? ((limitData.limit - limitData.remaining) / limitData.limit) * 100 : 100
|
|
|
|
const formatResetDate = (dateString: string | null) => {
|
|
if (!dateString) {
|
|
// If no reset date set, calculate 1 month from now
|
|
const nextMonth = new Date()
|
|
nextMonth.setMonth(nextMonth.getMonth() + 1)
|
|
return nextMonth.toLocaleDateString(locale, { year: 'numeric', month: 'long', day: 'numeric' })
|
|
}
|
|
const date = new Date(dateString)
|
|
return date.toLocaleDateString(locale, { year: 'numeric', month: 'long', day: 'numeric' })
|
|
}
|
|
|
|
return (
|
|
<Dialog
|
|
open={open}
|
|
onClose={onClose}
|
|
maxWidth="sm"
|
|
fullWidth
|
|
PaperProps={{
|
|
sx: {
|
|
borderRadius: 3,
|
|
p: 1
|
|
}
|
|
}}
|
|
>
|
|
<DialogTitle>
|
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
<Typography variant="h5" component="div" fontWeight="700">
|
|
{t('title')}
|
|
</Typography>
|
|
<Button
|
|
onClick={onClose}
|
|
sx={{ minWidth: 'auto', p: 1 }}
|
|
color="inherit"
|
|
>
|
|
<CloseIcon />
|
|
</Button>
|
|
</Box>
|
|
</DialogTitle>
|
|
|
|
<DialogContent>
|
|
{/* Current Usage */}
|
|
{limitData && (
|
|
<Box sx={{ mb: 4 }}>
|
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
|
|
<Typography variant="body2" color="text.secondary">
|
|
{t('conversationsUsed')}
|
|
</Typography>
|
|
<Typography variant="body2" fontWeight="600">
|
|
{limitData.limit - limitData.remaining} / {limitData.limit}
|
|
</Typography>
|
|
</Box>
|
|
<LinearProgress
|
|
variant="determinate"
|
|
value={usagePercentage}
|
|
sx={{
|
|
height: 8,
|
|
borderRadius: 1,
|
|
bgcolor: 'grey.200',
|
|
'& .MuiLinearProgress-bar': {
|
|
bgcolor: 'warning.main'
|
|
}
|
|
}}
|
|
/>
|
|
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
|
|
{t('resetsOn', { date: formatResetDate(limitData.resetDate) })}
|
|
</Typography>
|
|
</Box>
|
|
)}
|
|
|
|
{/* Limit Reached Message */}
|
|
<Box sx={{ mb: 4, textAlign: 'center' }}>
|
|
<Typography variant="body1" sx={{ mb: 2 }}>
|
|
{t('message')}
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary">
|
|
{t('upgradePrompt')}
|
|
</Typography>
|
|
</Box>
|
|
|
|
{/* Premium Benefits */}
|
|
<Box
|
|
sx={{
|
|
bgcolor: 'primary.light',
|
|
borderRadius: 2,
|
|
p: 3,
|
|
mb: 3
|
|
}}
|
|
>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
|
<AutoAwesome sx={{ color: 'primary.main', mr: 1 }} />
|
|
<Typography variant="h6" fontWeight="600">
|
|
{t('premiumTitle')}
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
<Box
|
|
sx={{
|
|
width: 6,
|
|
height: 6,
|
|
borderRadius: '50%',
|
|
bgcolor: 'primary.main',
|
|
mr: 1.5
|
|
}}
|
|
/>
|
|
<Typography variant="body2">
|
|
{t('benefits.unlimited')}
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
<Box
|
|
sx={{
|
|
width: 6,
|
|
height: 6,
|
|
borderRadius: '50%',
|
|
bgcolor: 'primary.main',
|
|
mr: 1.5
|
|
}}
|
|
/>
|
|
<Typography variant="body2">
|
|
{t('benefits.support')}
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
<Box
|
|
sx={{
|
|
width: 6,
|
|
height: 6,
|
|
borderRadius: '50%',
|
|
bgcolor: 'primary.main',
|
|
mr: 1.5
|
|
}}
|
|
/>
|
|
<Typography variant="body2">
|
|
{t('benefits.early')}
|
|
</Typography>
|
|
</Box>
|
|
</Box>
|
|
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
<Typography variant="h5" fontWeight="700" color="primary.main">
|
|
$10
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary">
|
|
{t('pricing')}
|
|
</Typography>
|
|
<Chip
|
|
label={t('savings')}
|
|
size="small"
|
|
color="success"
|
|
sx={{ ml: 'auto' }}
|
|
/>
|
|
</Box>
|
|
</Box>
|
|
</DialogContent>
|
|
|
|
<DialogActions sx={{ p: 3, pt: 0, flexDirection: 'column', gap: 2 }}>
|
|
<Button
|
|
variant="contained"
|
|
size="large"
|
|
fullWidth
|
|
startIcon={<Favorite />}
|
|
component={Link}
|
|
href={`/${locale}/subscription`}
|
|
onClick={onClose}
|
|
sx={{ py: 1.5 }}
|
|
>
|
|
{t('upgradeButton')}
|
|
</Button>
|
|
<Button
|
|
variant="text"
|
|
size="large"
|
|
fullWidth
|
|
onClick={onClose}
|
|
sx={{ py: 1 }}
|
|
>
|
|
{t('maybeLater')}
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
)
|
|
}
|