feat: add frontend subscription page - M4 (refs #55)
This commit is contained in:
101
frontend/src/features/subscription/components/TierCard.tsx
Normal file
101
frontend/src/features/subscription/components/TierCard.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import React from 'react';
|
||||
import { Card, CardContent, Typography, Button, Box, Chip, List, ListItem, ListItemIcon, ListItemText } from '@mui/material';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
import type { SubscriptionPlan, BillingCycle } from '../types/subscription.types';
|
||||
|
||||
interface TierCardProps {
|
||||
plan: SubscriptionPlan;
|
||||
billingCycle: BillingCycle;
|
||||
currentTier?: string;
|
||||
isLoading?: boolean;
|
||||
onUpgrade: () => void;
|
||||
}
|
||||
|
||||
export const TierCard: React.FC<TierCardProps> = ({
|
||||
plan,
|
||||
billingCycle,
|
||||
currentTier,
|
||||
isLoading = false,
|
||||
onUpgrade,
|
||||
}) => {
|
||||
const isCurrent = currentTier === plan.tier;
|
||||
const price = billingCycle === 'monthly' ? plan.monthlyPrice : plan.yearlyPrice;
|
||||
const priceLabel = billingCycle === 'monthly' ? '/month' : '/year';
|
||||
|
||||
return (
|
||||
<Card
|
||||
sx={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
border: isCurrent ? 2 : 1,
|
||||
borderColor: isCurrent ? 'primary.main' : 'divider',
|
||||
}}
|
||||
>
|
||||
{isCurrent && (
|
||||
<Chip
|
||||
label="Current Plan"
|
||||
color="primary"
|
||||
size="small"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 16,
|
||||
right: 16,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CardContent sx={{ flexGrow: 1, pt: isCurrent ? 6 : 3 }}>
|
||||
<Typography variant="h5" component="h3" gutterBottom fontWeight="bold">
|
||||
{plan.name}
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ my: 3 }}>
|
||||
<Typography variant="h3" component="div" fontWeight="bold">
|
||||
${price.toFixed(2)}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{priceLabel}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<List dense>
|
||||
{plan.features.map((feature, index) => (
|
||||
<ListItem key={index} disableGutters>
|
||||
<ListItemIcon sx={{ minWidth: 36 }}>
|
||||
<CheckCircleIcon color="primary" fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={feature}
|
||||
primaryTypographyProps={{ variant: 'body2' }}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</CardContent>
|
||||
|
||||
<CardContent sx={{ pt: 0 }}>
|
||||
{isCurrent ? (
|
||||
<Button
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
disabled
|
||||
>
|
||||
Current Plan
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant={plan.tier === 'enterprise' ? 'contained' : 'outlined'}
|
||||
color="primary"
|
||||
fullWidth
|
||||
disabled={isLoading}
|
||||
onClick={onUpgrade}
|
||||
>
|
||||
{plan.tier === 'free' ? 'Downgrade' : 'Upgrade'}
|
||||
</Button>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user