Enforce tier limits & green checkmarks on pricing
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled

Backend:
- Add portfolio limit enforcement (Scout: 0, Trader: 25, Tycoon: unlimited)
- Return proper error messages when limits exceeded

Frontend (Pricing):
- All checkmarks now green (text-accent)
- Comparison table uses Check icons instead of ✓ text
- Consistent visual styling across all tiers
This commit is contained in:
yves.gugger
2025-12-09 17:19:50 +01:00
parent 3d4b8ffc17
commit c9e30c951e
2 changed files with 48 additions and 9 deletions

View File

@ -262,6 +262,36 @@ async def add_portfolio_domain(
db: AsyncSession = Depends(get_db),
):
"""Add a domain to portfolio."""
from app.models.subscription import Subscription, SubscriptionTier, TIER_CONFIG
# Check subscription portfolio limit
await db.refresh(current_user, ["subscription"])
if current_user.subscription:
portfolio_limit = current_user.subscription.portfolio_limit
else:
portfolio_limit = TIER_CONFIG[SubscriptionTier.SCOUT].get("portfolio_limit", 0)
# Count current portfolio domains
count_result = await db.execute(
select(func.count(PortfolioDomain.id)).where(
PortfolioDomain.user_id == current_user.id
)
)
current_count = count_result.scalar() or 0
# Check limit (-1 means unlimited)
if portfolio_limit != -1 and current_count >= portfolio_limit:
if portfolio_limit == 0:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Portfolio feature not available on Scout plan. Upgrade to Trader or Tycoon.",
)
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"Portfolio limit reached ({portfolio_limit} domains). Upgrade to add more.",
)
# Check if domain already exists in user's portfolio
existing = await db.execute(
select(PortfolioDomain).where(

View File

@ -77,9 +77,9 @@ const comparisonFeatures = [
{ name: 'Watchlist Domains', scout: '5', trader: '50', tycoon: '500' },
{ name: 'Check Frequency', scout: 'Daily', trader: 'Hourly', tycoon: '10 min' },
{ name: 'Portfolio Domains', scout: '—', trader: '25', tycoon: 'Unlimited' },
{ name: 'Domain Valuation', scout: '—', trader: '', tycoon: '' },
{ name: 'Domain Valuation', scout: '—', trader: 'check', tycoon: 'check' },
{ name: 'Price History', scout: '—', trader: '90 days', tycoon: 'Unlimited' },
{ name: 'Expiry Tracking', scout: '—', trader: '', tycoon: '' },
{ name: 'Expiry Tracking', scout: '—', trader: 'check', tycoon: 'check' },
]
const faqs = [
@ -230,10 +230,7 @@ export default function PricingPage() {
<ul className="space-y-3 mb-8">
{tier.features.map((feature) => (
<li key={feature.text} className="flex items-start gap-3">
<Check className={clsx(
"w-4 h-4 mt-0.5 shrink-0",
feature.highlight ? "text-accent" : "text-foreground-muted"
)} strokeWidth={2.5} />
<Check className="w-4 h-4 mt-0.5 shrink-0 text-accent" strokeWidth={2.5} />
<span className={clsx(
"text-body-sm",
feature.highlight ? "text-foreground" : "text-foreground-muted"
@ -286,9 +283,21 @@ export default function PricingPage() {
{comparisonFeatures.map((feature) => (
<tr key={feature.name} className="border-b border-border/50">
<td className="py-4 px-4 text-body-sm text-foreground">{feature.name}</td>
<td className="py-4 px-4 text-center text-body-sm text-foreground-muted">{feature.scout}</td>
<td className="py-4 px-4 text-center text-body-sm text-foreground">{feature.trader}</td>
<td className="py-4 px-4 text-center text-body-sm text-foreground">{feature.tycoon}</td>
<td className="py-4 px-4 text-center text-body-sm text-foreground-muted">
{feature.scout === 'check' ? (
<Check className="w-5 h-5 text-accent mx-auto" strokeWidth={2.5} />
) : feature.scout}
</td>
<td className="py-4 px-4 text-center text-body-sm text-foreground">
{feature.trader === 'check' ? (
<Check className="w-5 h-5 text-accent mx-auto" strokeWidth={2.5} />
) : feature.trader}
</td>
<td className="py-4 px-4 text-center text-body-sm text-foreground">
{feature.tycoon === 'check' ? (
<Check className="w-5 h-5 text-accent mx-auto" strokeWidth={2.5} />
) : feature.tycoon}
</td>
</tr>
))}
</tbody>