summaryrefslogtreecommitdiff
path: root/Biz
diff options
context:
space:
mode:
Diffstat (limited to 'Biz')
-rw-r--r--Biz/PodcastItLater/Admin.py39
-rw-r--r--Biz/PodcastItLater/UI.py69
-rw-r--r--Biz/PodcastItLater/Web.py37
3 files changed, 89 insertions, 56 deletions
diff --git a/Biz/PodcastItLater/Admin.py b/Biz/PodcastItLater/Admin.py
index 7dd0c50..4920b39 100644
--- a/Biz/PodcastItLater/Admin.py
+++ b/Biz/PodcastItLater/Admin.py
@@ -12,6 +12,7 @@ Admin pages and functionality for managing users and queue items.
# : dep pytest-asyncio
# : dep pytest-mock
import Biz.PodcastItLater.Core as Core
+import Biz.PodcastItLater.UI as UI
import ludic.catalog.layouts as layouts
import ludic.html as html
@@ -290,16 +291,6 @@ def create_table_header(columns: list[str]) -> html.thead:
)
-def create_bootstrap_styles() -> html.style:
- """Load Bootstrap CSS for admin pages."""
- return html.style(
- "@import url('https://cdn.jsdelivr.net/npm/bootstrap@5.3.2"
- "/dist/css/bootstrap.min.css');"
- "@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons"
- "@1.11.3/font/bootstrap-icons.min.css');",
- )
-
-
class AdminUsers(Component[AnyChildren, AdminUsersAttrs]):
"""Admin view for managing users."""
@@ -314,15 +305,17 @@ class AdminUsers(Component[AnyChildren, AdminUsersAttrs]):
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",
+ html.meta(
+ name="color-scheme",
+ content="light dark",
),
+ html.title("PodcastItLater - User Management"),
+ UI.create_htmx_script(),
),
html.body(
- create_bootstrap_styles(),
+ UI.create_bootstrap_styles(),
+ # Auto dark mode CSS (must come after Bootstrap)
+ UI.create_auto_dark_mode_style(),
html.div(
html.h1(
"PodcastItLater - User Management",
@@ -390,15 +383,17 @@ class AdminView(Component[AnyChildren, AdminViewAttrs]):
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",
+ html.meta(
+ name="color-scheme",
+ content="light dark",
),
+ html.title("PodcastItLater - Admin Queue Status"),
+ UI.create_htmx_script(),
),
html.body(
- create_bootstrap_styles(),
+ UI.create_bootstrap_styles(),
+ # Auto dark mode CSS (must come after Bootstrap)
+ UI.create_auto_dark_mode_style(),
html.div(
AdminView._render_content(
queue_items,
diff --git a/Biz/PodcastItLater/UI.py b/Biz/PodcastItLater/UI.py
new file mode 100644
index 0000000..b7ec76c
--- /dev/null
+++ b/Biz/PodcastItLater/UI.py
@@ -0,0 +1,69 @@
+"""
+PodcastItLater Shared UI Components.
+
+Common UI components and utilities shared across web pages.
+"""
+
+# : out podcastitlater-ui
+# : dep ludic
+import ludic.html as html
+
+
+def create_bootstrap_styles() -> html.style:
+ """Load Bootstrap CSS and icons."""
+ return html.style(
+ "@import url('https://cdn.jsdelivr.net/npm/bootstrap@5.3.2"
+ "/dist/css/bootstrap.min.css');"
+ "@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons"
+ "@1.11.3/font/bootstrap-icons.min.css');",
+ )
+
+
+def create_auto_dark_mode_style() -> html.style:
+ """Create CSS for automatic dark mode based on prefers-color-scheme."""
+ return html.style(
+ """
+ /* Auto dark mode - applies Bootstrap dark theme via media query */
+ @media (prefers-color-scheme: dark) {
+ :root {
+ color-scheme: dark;
+ --bs-body-color: #dee2e6;
+ --bs-body-color-rgb: 222, 226, 230;
+ --bs-body-bg: #212529;
+ --bs-body-bg-rgb: 33, 37, 41;
+ --bs-emphasis-color: #fff;
+ --bs-emphasis-color-rgb: 255, 255, 255;
+ --bs-secondary-color: rgba(222, 226, 230, 0.75);
+ --bs-secondary-bg: #343a40;
+ --bs-tertiary-color: rgba(222, 226, 230, 0.5);
+ --bs-tertiary-bg: #2b3035;
+ --bs-heading-color: inherit;
+ --bs-link-color: #6ea8fe;
+ --bs-link-hover-color: #8bb9fe;
+ --bs-link-color-rgb: 110, 168, 254;
+ --bs-link-hover-color-rgb: 139, 185, 254;
+ --bs-code-color: #e685b5;
+ --bs-border-color: #495057;
+ --bs-border-color-translucent: rgba(255, 255, 255, 0.15);
+ }
+ }
+ """,
+ )
+
+
+def create_htmx_script() -> html.script:
+ """Load HTMX library."""
+ return html.script(
+ src="https://unpkg.com/htmx.org@1.9.10",
+ integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC",
+ crossorigin="anonymous",
+ )
+
+
+def create_bootstrap_js() -> html.script:
+ """Load Bootstrap JavaScript bundle."""
+ return html.script(
+ src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js",
+ integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL",
+ crossorigin="anonymous",
+ )
diff --git a/Biz/PodcastItLater/Web.py b/Biz/PodcastItLater/Web.py
index c4df33d..c1abf02 100644
--- a/Biz/PodcastItLater/Web.py
+++ b/Biz/PodcastItLater/Web.py
@@ -20,6 +20,7 @@ import Biz.EmailAgent
import Biz.PodcastItLater.Admin as Admin
import Biz.PodcastItLater.Billing as Billing
import Biz.PodcastItLater.Core as Core
+import Biz.PodcastItLater.UI as UI
import html as html_module
import httpx
import ludic.html as html
@@ -53,38 +54,6 @@ from typing import override
logger = Log.setup()
-def create_auto_dark_mode_style() -> html.style:
- """Create CSS for automatic dark mode based on prefers-color-scheme."""
- return html.style(
- """
- /* Auto dark mode - applies Bootstrap dark theme via media query */
- @media (prefers-color-scheme: dark) {
- :root {
- color-scheme: dark;
- --bs-body-color: #dee2e6;
- --bs-body-color-rgb: 222, 226, 230;
- --bs-body-bg: #212529;
- --bs-body-bg-rgb: 33, 37, 41;
- --bs-emphasis-color: #fff;
- --bs-emphasis-color-rgb: 255, 255, 255;
- --bs-secondary-color: rgba(222, 226, 230, 0.75);
- --bs-secondary-bg: #343a40;
- --bs-tertiary-color: rgba(222, 226, 230, 0.5);
- --bs-tertiary-bg: #2b3035;
- --bs-heading-color: inherit;
- --bs-link-color: #6ea8fe;
- --bs-link-hover-color: #8bb9fe;
- --bs-link-color-rgb: 110, 168, 254;
- --bs-link-hover-color-rgb: 139, 185, 254;
- --bs-code-color: #e685b5;
- --bs-border-color: #495057;
- --bs-border-color-translucent: rgba(255, 255, 255, 0.15);
- }
- }
- """,
- )
-
-
# Configuration
area = App.from_env()
BASE_URL = os.getenv("BASE_URL", "http://localhost:8000")
@@ -697,7 +666,7 @@ class BillingPage(Component[AnyChildren, BillingPageAttrs]):
"@1.11.3/font/bootstrap-icons.min.css');",
),
# Auto dark mode CSS (must come after Bootstrap)
- create_auto_dark_mode_style(),
+ UI.create_auto_dark_mode_style(),
html.div(
html.div(
html.h1(
@@ -1019,7 +988,7 @@ class HomePage(Component[AnyChildren, HomePageAttrs]):
"@1.11.3/font/bootstrap-icons.min.css');",
),
# Auto dark mode CSS (must come after Bootstrap)
- create_auto_dark_mode_style(),
+ UI.create_auto_dark_mode_style(),
# Bootstrap container
html.div(
# Header