diff options
| author | Ben Sima <ben@bsima.me> | 2025-11-09 17:23:36 -0500 |
|---|---|---|
| committer | Ben Sima <ben@bsima.me> | 2025-11-09 17:23:36 -0500 |
| commit | 83d90f815447abc5447f6b0b4a978b2e8ce82894 (patch) | |
| tree | 789c3b277680223dd771549d0180b58f198ac34e /Biz/PodcastItLater | |
| parent | 380e944671add8fd5dda66d8702fc7e02b219115 (diff) | |
feat(PodcastItLater): Add usage limit enforcement and billing UI
- Enforce tier limits before article submission - Display current
plan in user info card (Free/Personal/Pro) - Add Billing button to
dashboard navigation - Show friendly upgrade prompt when limit reached
- Link to /billing page for plan management
Limits enforced: - Free: 10 articles/month - Personal: 50
articles/month - Pro: Unlimited
Related to task t-144e7lF
Diffstat (limited to 'Biz/PodcastItLater')
| -rw-r--r-- | Biz/PodcastItLater/Web.py | 54 |
1 files changed, 50 insertions, 4 deletions
diff --git a/Biz/PodcastItLater/Web.py b/Biz/PodcastItLater/Web.py index c469874..03d3eb7 100644 --- a/Biz/PodcastItLater/Web.py +++ b/Biz/PodcastItLater/Web.py @@ -678,16 +678,41 @@ class HomePage(Component[AnyChildren, HomePageAttrs]): html.div( html.div( html.div( - html.p( + html.div( html.strong("Logged in as: "), user["email"], - classes=["mb-3"], + classes=["mb-2"], + ), + html.div( + html.i(classes=["bi", "bi-star", "me-2"]), + html.strong("Plan: "), + Billing.get_tier_info( + user.get("plan_tier", "free"), + )["name"], + classes=["mb-3", "text-muted", "small"], ), html.div( html.a( html.i( classes=[ "bi", + "bi-credit-card", + "me-1", + ], + ), + "Billing", + href="/billing", + classes=[ + "btn", + "btn-outline-secondary", + "btn-sm", + "me-2", + ], + ), + html.a( + html.i( + classes=[ + "bi", "bi-gear-fill", "me-1", ], @@ -965,7 +990,7 @@ def logout(request: Request) -> Response: @app.post("/submit") -def submit_article(request: Request, data: FormData) -> html.div: +def submit_article(request: Request, data: FormData) -> html.div: # noqa: PLR0911 """Handle manual form submission.""" try: # Check if user is logged in @@ -1004,6 +1029,26 @@ def submit_article(request: Request, data: FormData) -> html.div: classes=["alert", "alert-danger"], ) + # Check usage limits + allowed, _msg, usage = Billing.can_submit(user_id) + if not allowed: + tier = user.get("plan_tier", "free") + tier_info = Billing.get_tier_info(tier) + limit = tier_info.get("articles_limit", 0) + return html.div( + html.i(classes=["bi", "bi-exclamation-circle", "me-2"]), + html.strong("Limit reached: "), + f"You've used {usage['articles']}/{limit} articles " + "this period. ", + html.a( + "Upgrade your plan", + href="/billing", + classes=["alert-link"], + ), + " to continue.", + classes=["alert", "alert-warning"], + ) + # Extract Open Graph metadata title, author = extract_og_metadata(url) @@ -1154,7 +1199,8 @@ def billing_checkout(request: Request, data: FormData) -> Response: if not user_id: return Response("Unauthorized", status_code=401) - tier = data.get("tier", "personal") + tier_raw = data.get("tier", "personal") + tier = tier_raw if isinstance(tier_raw, str) else "personal" if tier not in {"personal", "pro"}: return Response("Invalid tier", status_code=400) |
