Subscription Guard and Installation Guide
This commit is contained in:
@@ -578,9 +578,37 @@ const Landing: React.FC = () => {
|
||||
background: `linear-gradient(180deg, ${alpha(theme.palette.background.default, 0.95)} 0%, ${alpha(theme.palette.background.paper, 0.98)} 100%)`,
|
||||
}}
|
||||
>
|
||||
<Suspense fallback={<LoadingSpinner />}>
|
||||
{React.createElement(lazy(() => import('../Pricing/PricingPage')))}
|
||||
</Suspense>
|
||||
<Container maxWidth="lg">
|
||||
<Box sx={{ textAlign: 'center', mb: 6 }}>
|
||||
<Typography variant="h3" component="h2" gutterBottom fontWeight={700}>
|
||||
Choose Your Plan
|
||||
</Typography>
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
Start with a free plan or upgrade for advanced features
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ textAlign: 'center' }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
onClick={() => window.location.href = '/pricing'}
|
||||
sx={{
|
||||
px: 6,
|
||||
py: 2,
|
||||
fontSize: '1.1rem',
|
||||
fontWeight: 600,
|
||||
background: `linear-gradient(135deg, ${theme.palette.primary.main} 0%, ${theme.palette.secondary.main} 100%)`,
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${theme.palette.primary.dark} 0%, ${theme.palette.secondary.dark} 100%)`,
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: `0 8px 24px ${alpha(theme.palette.primary.main, 0.4)}`,
|
||||
}
|
||||
}}
|
||||
>
|
||||
View All Plans & Features
|
||||
</Button>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
|
||||
{/* Introducing ALwrity Section with Background - Lazy Loaded */}
|
||||
|
||||
@@ -82,6 +82,7 @@ const PricingPage: React.FC = () => {
|
||||
const [selectedPlan, setSelectedPlan] = useState<number | null>(null);
|
||||
const [subscribing, setSubscribing] = useState(false);
|
||||
const [paymentModalOpen, setPaymentModalOpen] = useState(false);
|
||||
const [showSignInPrompt, setShowSignInPrompt] = useState(false);
|
||||
const [knowMoreModal, setKnowMoreModal] = useState<{ open: boolean; title: string; content: React.ReactNode }>({
|
||||
open: false,
|
||||
title: '',
|
||||
@@ -113,6 +114,17 @@ const PricingPage: React.FC = () => {
|
||||
const plan = plans.find(p => p.id === planId);
|
||||
if (!plan) return;
|
||||
|
||||
// Get user_id from localStorage (set by Clerk auth)
|
||||
const userId = localStorage.getItem('user_id');
|
||||
|
||||
// Check if user is signed in
|
||||
if (!userId || userId === 'anonymous' || userId === '') {
|
||||
// User not signed in, show sign-in prompt
|
||||
console.warn('PricingPage: User not signed in, showing prompt');
|
||||
setShowSignInPrompt(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// For alpha testing, only allow Free and Basic plans (Pro features not ready)
|
||||
if (plan.tier !== 'free' && plan.tier !== 'basic') {
|
||||
setError('This plan is not available for alpha testing');
|
||||
@@ -937,6 +949,38 @@ const PricingPage: React.FC = () => {
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* Sign In Prompt Modal */}
|
||||
<Dialog
|
||||
open={showSignInPrompt}
|
||||
onClose={() => setShowSignInPrompt(false)}
|
||||
maxWidth="sm"
|
||||
fullWidth
|
||||
>
|
||||
<DialogTitle>Sign In Required</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="body1" sx={{ mb: 2 }}>
|
||||
Please sign in to subscribe to a plan and start using ALwrity.
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
If you don't have an account, signing in will automatically create one for you.
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setShowSignInPrompt(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
// Redirect to landing page which has sign-in
|
||||
window.location.href = '/';
|
||||
}}
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Avatar, Box, Menu, MenuItem, Typography, Tooltip } from '@mui/material';
|
||||
import { Avatar, Box, Menu, MenuItem, Typography, Tooltip, Chip } from '@mui/material';
|
||||
import { useUser, useClerk } from '@clerk/clerk-react';
|
||||
import { useSubscription } from '../../contexts/SubscriptionContext';
|
||||
|
||||
interface UserBadgeProps {
|
||||
colorMode?: 'light' | 'dark';
|
||||
@@ -9,6 +10,7 @@ interface UserBadgeProps {
|
||||
const UserBadge: React.FC<UserBadgeProps> = ({ colorMode = 'light' }) => {
|
||||
const { user, isSignedIn } = useUser();
|
||||
const { signOut } = useClerk();
|
||||
const { subscription } = useSubscription();
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
@@ -20,6 +22,22 @@ const UserBadge: React.FC<UserBadgeProps> = ({ colorMode = 'light' }) => {
|
||||
|
||||
if (!isSignedIn) return null;
|
||||
|
||||
// Get plan display info
|
||||
const getPlanColor = () => {
|
||||
switch (subscription?.plan) {
|
||||
case 'free': return '#4caf50';
|
||||
case 'basic': return '#2196f3';
|
||||
case 'pro': return '#9c27b0';
|
||||
case 'enterprise': return '#ff9800';
|
||||
default: return '#757575';
|
||||
}
|
||||
};
|
||||
|
||||
const getPlanLabel = () => {
|
||||
if (!subscription?.active) return 'No Plan';
|
||||
return subscription.plan.charAt(0).toUpperCase() + subscription.plan.slice(1);
|
||||
};
|
||||
|
||||
const handleOpen = (e: React.MouseEvent<HTMLElement>) => setAnchorEl(e.currentTarget);
|
||||
const handleClose = () => setAnchorEl(null);
|
||||
|
||||
@@ -33,6 +51,20 @@ const UserBadge: React.FC<UserBadgeProps> = ({ colorMode = 'light' }) => {
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
{/* Subscription Plan Chip */}
|
||||
<Chip
|
||||
label={getPlanLabel()}
|
||||
size="small"
|
||||
sx={{
|
||||
bgcolor: `${getPlanColor()}20`,
|
||||
border: `1px solid ${getPlanColor()}`,
|
||||
color: getPlanColor(),
|
||||
fontWeight: 700,
|
||||
fontSize: '0.75rem',
|
||||
height: 24,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Tooltip title={`${user?.fullName || user?.username || user?.primaryEmailAddress?.emailAddress || 'User'}`}>
|
||||
<Avatar
|
||||
onClick={handleOpen}
|
||||
@@ -49,8 +81,9 @@ const UserBadge: React.FC<UserBadgeProps> = ({ colorMode = 'light' }) => {
|
||||
{initials}
|
||||
</Avatar>
|
||||
</Tooltip>
|
||||
|
||||
<Menu anchorEl={anchorEl} open={open} onClose={handleClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
|
||||
<Box sx={{ px: 2, py: 1 }}>
|
||||
<Box sx={{ px: 2, py: 1, borderBottom: '1px solid rgba(0,0,0,0.1)' }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
|
||||
{user?.fullName || user?.username || 'User'}
|
||||
</Typography>
|
||||
@@ -58,7 +91,28 @@ const UserBadge: React.FC<UserBadgeProps> = ({ colorMode = 'light' }) => {
|
||||
{user?.primaryEmailAddress?.emailAddress}
|
||||
</Typography>
|
||||
</Box>
|
||||
<MenuItem onClick={handleClose}>Signed in</MenuItem>
|
||||
|
||||
{/* Subscription Info in Menu */}
|
||||
<Box sx={{ px: 2, py: 1.5, bgcolor: 'rgba(0,0,0,0.02)' }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 0.5 }}>
|
||||
Current Plan
|
||||
</Typography>
|
||||
<Chip
|
||||
label={getPlanLabel()}
|
||||
size="small"
|
||||
sx={{
|
||||
bgcolor: `${getPlanColor()}20`,
|
||||
border: `1px solid ${getPlanColor()}`,
|
||||
color: getPlanColor(),
|
||||
fontWeight: 700,
|
||||
fontSize: '0.75rem',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<MenuItem onClick={() => { handleClose(); window.location.href = '/pricing'; }}>
|
||||
Manage Subscription
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleSignOut}>Sign out</MenuItem>
|
||||
</Menu>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user