diff options
Diffstat (limited to 'Biz/PodcastItLater/UI.py')
| -rw-r--r-- | Biz/PodcastItLater/UI.py | 323 |
1 files changed, 158 insertions, 165 deletions
diff --git a/Biz/PodcastItLater/UI.py b/Biz/PodcastItLater/UI.py index bdf7a5b..cbab2a2 100644 --- a/Biz/PodcastItLater/UI.py +++ b/Biz/PodcastItLater/UI.py @@ -91,7 +91,7 @@ def create_auto_dark_mode_style() -> html.style: /* Navbar dark mode */ .navbar.bg-body-tertiary { - background-color: #2b3035 !important; + background-color: #2b3035 !important; } .navbar .navbar-text { @@ -128,7 +128,6 @@ def create_bootstrap_js() -> html.script: ) - class PageLayoutAttrs(Attrs): """Attributes for PageLayout component.""" @@ -143,6 +142,78 @@ class PageLayout(Component[AnyChildren, PageLayoutAttrs]): """Reusable page layout with header and navbar.""" @staticmethod + def _render_nav_item( + label: str, + href: str, + icon: str, + *, + is_active: bool, + ) -> html.li: + return html.li( + html.a( + html.i(classes=["bi", f"bi-{icon}", "me-1"]), + label, + href=href, + classes=[ + "nav-link", + "active" if is_active else "", + ], + ), + classes=["nav-item"], + ) + + @staticmethod + def _render_admin_dropdown( + is_active_func: typing.Callable[[str], bool], + ) -> html.li: + is_active = is_active_func("admin") or is_active_func("admin-users") + return html.li( + html.a( # type: ignore[call-arg] + html.i(classes=["bi", "bi-gear-fill", "me-1"]), + "Admin", + href="#", + id="adminDropdown", + role="button", + data_bs_toggle="dropdown", + aria_expanded="false", + classes=[ + "nav-link", + "dropdown-toggle", + "active" if is_active else "", + ], + ), + html.ul( # type: ignore[call-arg] + html.li( + html.a( + html.i(classes=["bi", "bi-list-task", "me-2"]), + "Queue Status", + href="/admin", + classes=["dropdown-item"], + ), + ), + html.li( + html.a( + html.i(classes=["bi", "bi-people-fill", "me-2"]), + "Manage Users", + href="/admin/users", + classes=["dropdown-item"], + ), + ), + html.li( + html.a( + html.i(classes=["bi", "bi-graph-up", "me-2"]), + "Metrics", + href="/admin/metrics", + classes=["dropdown-item"], + ), + ), + classes=["dropdown-menu"], + aria_labelledby="adminDropdown", + ), + classes=["nav-item", "dropdown"], + ) + + @staticmethod def _render_navbar( user: dict[str, typing.Any] | None, current_page: str, @@ -166,150 +237,31 @@ class PageLayout(Component[AnyChildren, PageLayoutAttrs]): ), html.div( html.ul( - html.li( - html.a( - html.i( - classes=[ - "bi", - "bi-house-fill", - "me-1", - ], - ), - "Home", - href="/", - classes=[ - "nav-link", - "active" if is_active("home") else "", - ], - ), - classes=["nav-item"], + PageLayout._render_nav_item( + "Home", + "/", + "house-fill", + is_active=is_active("home"), ), - html.li( - html.a( - html.i( - classes=[ - "bi", - "bi-globe", - "me-1", - ], - ), - "Public Feed", - href="/public", - classes=[ - "nav-link", - "active" if is_active("public") else "", - ], - ), - classes=["nav-item"], + PageLayout._render_nav_item( + "Public Feed", + "/public", + "globe", + is_active=is_active("public"), ), - html.li( - html.a( - html.i( - classes=[ - "bi", - "bi-stars", - "me-1", - ], - ), - "Pricing", - href="/pricing", - classes=[ - "nav-link", - "active" if is_active("pricing") else "", - ], - ), - classes=["nav-item"], + PageLayout._render_nav_item( + "Pricing", + "/pricing", + "stars", + is_active=is_active("pricing"), ), - html.li( - html.a( - html.i( - classes=[ - "bi", - "bi-person-circle", - "me-1", - ], - ), - "Manage Account", - href="/account", - classes=[ - "nav-link", - "active" if is_active("account") else "", - ], - ), - classes=["nav-item"], + PageLayout._render_nav_item( + "Manage Account", + "/account", + "person-circle", + is_active=is_active("account"), ), - html.li( - html.a( # type: ignore[call-arg] - html.i( - classes=[ - "bi", - "bi-gear-fill", - "me-1", - ], - ), - "Admin", - href="#", - id="adminDropdown", - role="button", - data_bs_toggle="dropdown", - aria_expanded="false", - classes=[ - "nav-link", - "dropdown-toggle", - "active" - if is_active("admin") - or is_active("admin-users") - else "", - ], - ), - html.ul( # type: ignore[call-arg] - html.li( - html.a( - html.i( - classes=[ - "bi", - "bi-list-task", - "me-2", - ], - ), - "Queue Status", - href="/admin", - classes=["dropdown-item"], - ), - ), - html.li( - html.a( - html.i( - classes=[ - "bi", - "bi-people-fill", - "me-2", - ], - ), - "Manage Users", - href="/admin/users", - classes=["dropdown-item"], - ), - ), - html.li( - html.a( - html.i( - classes=[ - "bi", - "bi-graph-up", - "me-2", - ], - ), - "Metrics", - href="/admin/metrics", - classes=["dropdown-item"], - ), - ), - classes=["dropdown-menu"], - aria_labelledby="adminDropdown", - ), - classes=["nav-item", "dropdown"], - ) + PageLayout._render_admin_dropdown(is_active) if user and Core.is_admin(user) else html.span(), classes=["navbar-nav"], @@ -420,12 +372,22 @@ class AccountPage(Component[AnyChildren, AccountPageAttrs]): 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) - + + limit_text = ( + "Unlimited" if article_limit is None else str(article_limit) + ) + + usage_percent = 0 + if article_limit: + usage_percent = min(100, int((article_usage / article_limit) * 100)) + + progress_style = ( + {"width": f"{usage_percent}%"} if article_limit else {"width": "0%"} + ) + return PageLayout( html.div( html.div( @@ -437,8 +399,8 @@ class AccountPage(Component[AnyChildren, AccountPageAttrs]): classes=[ "bi", "bi-person-circle", - "me-2" - ] + "me-2", + ], ), "My Account", classes=["card-title", "mb-4"], @@ -453,7 +415,9 @@ class AccountPage(Component[AnyChildren, AccountPageAttrs]): ), html.p( html.strong("Member since: "), - user.get("created_at", "").split("T")[0], + user.get("created_at", "").split("T")[ + 0 + ], classes=["mb-4"], ), classes=["mb-5"], @@ -488,20 +452,26 @@ class AccountPage(Component[AnyChildren, AccountPageAttrs]): ), html.div( html.div( - f"{article_usage} / {limit_text}", + f"{article_usage} / " + f"{limit_text}", classes=["mb-1"], ), html.div( html.div( - classes=["progress-bar"], + classes=[ + "progress-bar", + ], role="progressbar", - style={ - "width": f"{min(100, (article_usage / article_limit * 100))}%" - } if article_limit else {"width": "0%"}, + style=progress_style, ), - classes=["progress", "mb-3"], + classes=[ + "progress", + "mb-3", + ], style={"height": "10px"}, - ) if article_limit else html.div(), + ) + if article_limit + else html.div(), classes=["mb-3"], ), ), @@ -509,23 +479,43 @@ class AccountPage(Component[AnyChildren, AccountPageAttrs]): html.div( html.form( html.button( - html.i(classes=["bi", "bi-credit-card", "me-2"]), + html.i( + classes=[ + "bi", + "bi-credit-card", + "me-2", + ], + ), "Manage Subscription", type="submit", - classes=["btn", "btn-outline-primary"], + 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"]), + ) + 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=[ + "card", + "card-body", + "bg-light", + ], ), classes=["mb-5"], ), @@ -537,12 +527,15 @@ class AccountPage(Component[AnyChildren, AccountPageAttrs]): classes=[ "bi", "bi-box-arrow-right", - "me-2" - ] + "me-2", + ], ), "Log Out", type="submit", - classes=["btn", "btn-outline-danger"], + classes=[ + "btn", + "btn-outline-danger", + ], ), action="/logout", method="post", |
