Files
ALwrity/frontend/src/components/SubscriptionGuard.tsx
2025-10-13 10:25:57 +05:30

167 lines
4.1 KiB
TypeScript

import React, { ReactNode } from 'react';
import {
Box,
Typography,
Button,
Alert,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Card,
CardContent,
CardActions,
Chip,
LinearProgress
} from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { useSubscriptionGuard, SubscriptionGuardOptions } from '../hooks/useSubscriptionGuard';
import { Lock as LockIcon, Upgrade as UpgradeIcon } from '@mui/icons-material';
interface SubscriptionGuardProps extends SubscriptionGuardOptions {
children: ReactNode;
feature?: string;
fallbackMessage?: string;
showUpgradeButton?: boolean;
showUsageProgress?: boolean;
}
export const SubscriptionGuard: React.FC<SubscriptionGuardProps> = ({
children,
feature,
fallbackMessage,
showUpgradeButton = true,
showUsageProgress = false,
...guardOptions
}) => {
const navigate = useNavigate();
const {
subscription,
loading,
isGuarded,
checkFeatureAccess,
getRemainingUsage,
checkSubscription
} = useSubscriptionGuard(guardOptions);
if (loading) {
return (
<Box sx={{ p: 3 }}>
<LinearProgress />
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
Checking subscription...
</Typography>
</Box>
);
}
if (isGuarded) {
if (fallbackMessage) {
return (
<Alert severity="warning" sx={{ mb: 2 }}>
{fallbackMessage}
</Alert>
);
}
return (
<Card sx={{ maxWidth: 400, mx: 'auto', mt: 2 }}>
<CardContent>
<Box sx={{ textAlign: 'center', mb: 2 }}>
<LockIcon sx={{ fontSize: 48, color: 'warning.main', mb: 1 }} />
<Typography variant="h6" gutterBottom>
Feature Locked
</Typography>
<Typography variant="body2" color="text.secondary">
This feature requires an active subscription.
</Typography>
</Box>
{subscription && (
<Box sx={{ mb: 2 }}>
<Typography variant="body2" gutterBottom>
Current Plan: <Chip label={subscription.plan} size="small" />
</Typography>
{subscription.reason && (
<Typography variant="body2" color="error">
{subscription.reason}
</Typography>
)}
</Box>
)}
</CardContent>
{showUpgradeButton && (
<CardActions sx={{ justifyContent: 'center', pb: 2 }}>
<Button
variant="contained"
startIcon={<UpgradeIcon />}
onClick={() => {
navigate('/pricing');
}}
>
Upgrade Plan
</Button>
</CardActions>
)}
</Card>
);
}
if (feature && !checkFeatureAccess(feature)) {
const remaining = getRemainingUsage(feature);
return (
<Alert severity="warning" sx={{ mb: 2 }}>
<Typography variant="body2">
{fallbackMessage || `You've reached your limit for ${feature}. Upgrade to continue using this feature.`}
</Typography>
{showUpgradeButton && (
<Button
size="small"
variant="outlined"
sx={{ mt: 1 }}
onClick={() => {
navigate('/pricing');
}}
>
Upgrade
</Button>
)}
</Alert>
);
}
return <>{children}</>;
};
// Convenience component for protecting entire sections
export const ProtectedSection: React.FC<{
children: ReactNode;
feature?: string;
title?: string;
}> = ({ children, feature, title }) => {
return (
<SubscriptionGuard feature={feature}>
<Box>
{title && (
<Typography variant="h6" gutterBottom>
{title}
</Typography>
)}
{children}
</Box>
</SubscriptionGuard>
);
};
// Hook for checking if user can perform an action
export const useCanPerformAction = (action: string) => {
const { subscription, isFeatureAvailable } = useSubscriptionGuard();
return {
canPerform: subscription?.active && isFeatureAvailable(action),
subscription,
};
};