diff options
| author | Ben Sima <ben@bsima.me> | 2025-11-09 20:32:34 -0500 |
|---|---|---|
| committer | Ben Sima <ben@bsima.me> | 2025-11-09 20:32:34 -0500 |
| commit | 14af38a21dc8e790b9ddc29e241784fd769dc3fc (patch) | |
| tree | e33ef9866f6448088cac4acd2ef9b48c32ca3990 /Biz/PodcastItLater/Admin.py | |
| parent | 83d90f815447abc5447f6b0b4a978b2e8ce82894 (diff) | |
PodcastItLater: Add Stripe billing and mobile responsiveness
- Implemented complete Stripe integration (Billing.py)
- Checkout sessions for subscription upgrades - Billing portal for
subscription management - Webhook handling for subscription events
- Usage tracking with tier-based limits (free: 10, personal: 50,
pro: unlimited)
- Added billing page UI (BillingPage component)
- Current plan display with usage stats - Pricing cards for all
tiers with upgrade buttons - Manage subscription button for paid
users - Success/error messaging
- Database migrations for billing
- Added plan_tier, stripe_customer_id, stripe_subscription_id - Added
subscription_status, period dates, cancel_at_period_end - Created
stripe_events table for webhook idempotency - Added get_usage()
method for usage tracking
- Made UI mobile-friendly and responsive
- Added viewport meta tags to all pages - Replaced pages.HtmlPage
with raw html.html for meta tag control - Responsive button layouts
with flexbox wrapping - Responsive pricing cards (1 col mobile,
2 col tablet, 3 col desktop) - Touch-friendly forms and buttons
(44px minimum) - Responsive padding and containers - Admin tables
with horizontal scroll
- Added Stripe testing guide (STRIPE_TESTING.md)
- Fixed CSS bug in pricing cards (cardh-100 text rendering) - Updated
tasks: completed t-144e7lF, t-1pIV0ZF, t-1s8ADC0
Amp-Thread-ID:
https://ampcode.com/threads/T-42fd5fb3-3dc5-4cbc-a9a3-78db9e13187e
Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'Biz/PodcastItLater/Admin.py')
| -rw-r--r-- | Biz/PodcastItLater/Admin.py | 45 |
1 files changed, 30 insertions, 15 deletions
diff --git a/Biz/PodcastItLater/Admin.py b/Biz/PodcastItLater/Admin.py index 5772256..7dd0c50 100644 --- a/Biz/PodcastItLater/Admin.py +++ b/Biz/PodcastItLater/Admin.py @@ -13,7 +13,6 @@ Admin pages and functionality for managing users and queue items. # : dep pytest-mock import Biz.PodcastItLater.Core as Core import ludic.catalog.layouts as layouts -import ludic.catalog.pages as pages import ludic.html as html # i need to import these unused because bild cannot get local transitive python @@ -305,16 +304,24 @@ class AdminUsers(Component[AnyChildren, AdminUsersAttrs]): """Admin view for managing users.""" @override - def render(self) -> pages.HtmlPage: + def render(self) -> html.html: users = self.attrs["users"] - return pages.HtmlPage( - pages.Head( - title="PodcastItLater - User Management", - htmx_version="1.9.10", - load_styles=False, + return html.html( + html.head( + html.meta(charset="utf-8"), + html.meta( + name="viewport", + content="width=device-width, initial-scale=1", + ), + html.title("PodcastItLater - User Management"), + html.script( + src="https://unpkg.com/htmx.org@1.9.10", + integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC", + crossorigin="anonymous", + ), ), - pages.Body( + html.body( create_bootstrap_styles(), html.div( html.h1( @@ -371,18 +378,26 @@ class AdminView(Component[AnyChildren, AdminViewAttrs]): """Admin view showing all queue items and episodes in tables.""" @override - def render(self) -> pages.HtmlPage: + def render(self) -> html.html: queue_items = self.attrs["queue_items"] episodes = self.attrs["episodes"] status_counts = self.attrs.get("status_counts", {}) - return pages.HtmlPage( - pages.Head( - title="PodcastItLater - Admin Queue Status", - htmx_version="1.9.10", - load_styles=False, + return html.html( + html.head( + html.meta(charset="utf-8"), + html.meta( + name="viewport", + content="width=device-width, initial-scale=1", + ), + html.title("PodcastItLater - Admin Queue Status"), + html.script( + src="https://unpkg.com/htmx.org@1.9.10", + integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC", + crossorigin="anonymous", + ), ), - pages.Body( + html.body( create_bootstrap_styles(), html.div( AdminView._render_content( |
