diff options
Diffstat (limited to 'Biz/PodcastItLater/UI.py')
| -rw-r--r-- | Biz/PodcastItLater/UI.py | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/Biz/PodcastItLater/UI.py b/Biz/PodcastItLater/UI.py index 27f5fff..4650708 100644 --- a/Biz/PodcastItLater/UI.py +++ b/Biz/PodcastItLater/UI.py @@ -407,6 +407,173 @@ class PageLayout(Component[AnyChildren, PageLayoutAttrs]): ) +class AccountPageAttrs(Attrs): + """Attributes for AccountPage component.""" + + user: dict[str, typing.Any] + usage: dict[str, int] + limits: dict[str, int | None] + portal_url: str | None + + +class AccountPage(Component[AnyChildren, AccountPageAttrs]): + """Account management page component.""" + + @override + def render(self) -> PageLayout: + user = self.attrs["user"] + usage = self.attrs["usage"] + limits = self.attrs["limits"] + portal_url = self.attrs["portal_url"] + + plan_tier = user.get("plan_tier", "free") + is_paid = plan_tier == "paid" + + article_limit = limits.get("articles_per_period") + article_usage = usage.get("articles", 0) + + limit_text = "Unlimited" if article_limit is None else str(article_limit) + + return PageLayout( + user=user, + current_page="account", + page_title="Account - PodcastItLater", + error=None, + meta_tags=[], + children=[ + html.div( + html.div( + html.div( + html.div( + html.h2( + html.i( + classes=[ + "bi", + "bi-person-circle", + "me-2" + ] + ), + "My Account", + classes=["card-title", "mb-4"], + ), + # User Info Section + html.div( + html.h5("Profile", classes=["mb-3"]), + html.p( + html.strong("Email: "), + user.get("email", ""), + classes=["mb-2"], + ), + html.p( + html.strong("Member since: "), + user.get("created_at", "").split("T")[0], + classes=["mb-4"], + ), + classes=["mb-5"], + ), + # Subscription Section + html.div( + html.h5("Subscription", classes=["mb-3"]), + html.div( + html.div( + html.strong("Current Plan"), + html.span( + plan_tier.title(), + classes=[ + "badge", + "bg-success" + if is_paid + else "bg-secondary", + "ms-2", + ], + ), + classes=[ + "d-flex", + "align-items-center", + "mb-3", + ], + ), + # Usage Stats + html.div( + html.p( + "Usage this period:", + classes=["mb-2", "text-muted"], + ), + html.div( + html.div( + f"{article_usage} / {limit_text}", + classes=["mb-1"], + ), + html.div( + html.div( + classes=["progress-bar"], + role="progressbar", + style={ + "width": f"{min(100, (article_usage / article_limit * 100))}%" + } if article_limit else {"width": "0%"}, + ), + classes=["progress", "mb-3"], + style={"height": "10px"}, + ) if article_limit else html.div(), + classes=["mb-3"], + ), + ), + # Actions + html.div( + html.form( + html.button( + html.i(classes=["bi", "bi-credit-card", "me-2"]), + "Manage Subscription", + type="submit", + classes=["btn", "btn-outline-primary"], + ), + method="POST", + action=portal_url, + ) if is_paid and portal_url else + html.a( + html.i(classes=["bi", "bi-star-fill", "me-2"]), + "Upgrade to Pro", + href="/pricing", + classes=["btn", "btn-primary"], + ), + classes=["d-flex", "gap-2"], + ), + classes=["card", "card-body", "bg-light"], + ), + classes=["mb-5"], + ), + # Logout Section + html.div( + html.form( + html.button( + html.i( + classes=[ + "bi", + "bi-box-arrow-right", + "me-2" + ] + ), + "Log Out", + type="submit", + classes=["btn", "btn-outline-danger"], + ), + action="/logout", + method="POST", + ), + classes=["border-top", "pt-4"], + ), + classes=["card-body", "p-4"], + ), + classes=["card", "shadow-sm"], + ), + classes=["col-lg-8", "mx-auto"], + ), + classes=["row"], + ), + ], + ) + + class PricingPageAttrs(Attrs): """Attributes for PricingPage component.""" |
