summaryrefslogtreecommitdiff
path: root/Biz/PodcastItLater/UI.py
diff options
context:
space:
mode:
Diffstat (limited to 'Biz/PodcastItLater/UI.py')
-rw-r--r--Biz/PodcastItLater/UI.py181
1 files changed, 174 insertions, 7 deletions
diff --git a/Biz/PodcastItLater/UI.py b/Biz/PodcastItLater/UI.py
index 59de7e5..bdf7a5b 100644
--- a/Biz/PodcastItLater/UI.py
+++ b/Biz/PodcastItLater/UI.py
@@ -399,6 +399,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(
+ html.div(
+ 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"],
+ ),
+ ),
+ user=user,
+ current_page="account",
+ page_title="Account - PodcastItLater",
+ error=None,
+ meta_tags=[],
+ )
+
+
class PricingPageAttrs(Attrs):
"""Attributes for PricingPage component."""
@@ -414,12 +581,7 @@ class PricingPage(Component[AnyChildren, PricingPageAttrs]):
current_tier = user.get("plan_tier", "free") if user else "free"
return PageLayout(
- user=user,
- current_page="pricing",
- page_title="Pricing - PodcastItLater",
- error=None,
- meta_tags=[],
- children=[
+ html.div(
html.div(
html.h2("Simple Pricing", classes=["text-center", "mb-5"]),
html.div(
@@ -539,5 +701,10 @@ class PricingPage(Component[AnyChildren, PricingPageAttrs]):
),
classes=["container", "py-3"],
),
- ],
+ ),
+ user=user,
+ current_page="pricing",
+ page_title="Pricing - PodcastItLater",
+ error=None,
+ meta_tags=[],
)