From 0baab1972e30c0e4629e67152838e660b02a2537 Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Mon, 15 Dec 2025 08:47:02 -0500 Subject: t-265.6: Add feedback collection endpoint for PIL - Add feedback table with migration in Core.py - Add FeedbackForm and FeedbackPage UI components - Add /feedback GET/POST routes and /api/feedback JSON endpoint - Add admin feedback view at /admin/feedback - Create Omni/Agent/Tools/Feedback.hs with feedback_list tool - Wire feedback tool into Telegram agent --- Biz/PodcastItLater/Admin/Views.py | 89 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) (limited to 'Biz/PodcastItLater/Admin/Views.py') diff --git a/Biz/PodcastItLater/Admin/Views.py b/Biz/PodcastItLater/Admin/Views.py index 7cc71a5..7834340 100644 --- a/Biz/PodcastItLater/Admin/Views.py +++ b/Biz/PodcastItLater/Admin/Views.py @@ -742,3 +742,92 @@ class AdminView(Component[AnyChildren, AdminViewAttrs]): ), ), ) + + +class AdminFeedbackAttrs(Attrs): + """Attributes for AdminFeedback component.""" + + feedback: list[dict[str, typing.Any]] + count: int + user: dict[str, typing.Any] + + +class AdminFeedback(Component[AnyChildren, AdminFeedbackAttrs]): + """Admin feedback listing page.""" + + @override + def render(self) -> UI.PageLayout: + feedback = self.attrs["feedback"] + count = self.attrs["count"] + user = self.attrs["user"] + + rows = [ + html.tr( + html.td(html.small(fb["id"][:8], classes=["text-muted"])), + html.td(fb.get("email") or "-"), + html.td( + html.span( + fb.get("source") or "-", + classes=["badge", "bg-secondary"], + ), + ), + html.td(str(fb.get("rating") or "-")), + html.td( + TruncatedText( + text=fb.get("use_case") or "-", + max_length=50, + ), + ), + html.td( + TruncatedText( + text=fb.get("feedback_text") or "-", + max_length=50, + ), + ), + html.td( + html.small( + fb["created_at"], + classes=["text-muted"], + ), + ), + ) + for fb in feedback + ] + + return UI.PageLayout( + html.div( + html.h2( + html.i(classes=["bi", "bi-chat-heart-fill", "me-2"]), + f"Feedback ({count})", + classes=["mb-4"], + ), + html.div( + html.table( + create_table_header([ + "ID", + "Email", + "Source", + "Rating", + "Use Case", + "Feedback", + "Created", + ]), + html.tbody(*rows), + classes=["table", "table-hover", "table-sm"], + ), + classes=["table-responsive"], + ) + if rows + else html.div( + html.p( + "No feedback yet.", + classes=["text-muted", "text-center", "py-5"], + ), + ), + ), + user=user, + current_page="admin", + page_title="Admin Feedback - PodcastItLater", + error=None, + meta_tags=[], + ) -- cgit v1.2.3 From 32c2bb198007ab85095c14be544cfca9d083a7cd Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Wed, 17 Dec 2025 11:00:29 -0500 Subject: Improve PodcastItLater homepage and dark mode support Homepage changes: - New marketing-focused landing page for logged-out users - Hero: 'Your Reading List, as a Podcast' with clear value prop - How It Works: 3-step process cards (paste URL, convert, listen) - Social proof: 'Join others' section with public feed - Login form moved below social proof for better conversion flow Dark mode fixes: - Remove bg-light from Current Plan card on /account page - Remove bg-white from metrics card headers on /admin/metrics Layout improvements: - Stack metrics tables vertically (full-width) instead of horizontal - Add spacing between metric cards for better readability --- Biz/PodcastItLater/Admin/Views.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'Biz/PodcastItLater/Admin/Views.py') diff --git a/Biz/PodcastItLater/Admin/Views.py b/Biz/PodcastItLater/Admin/Views.py index 7834340..057c5e0 100644 --- a/Biz/PodcastItLater/Admin/Views.py +++ b/Biz/PodcastItLater/Admin/Views.py @@ -263,16 +263,16 @@ class MetricsDashboard(Component[AnyChildren, MetricsAttrs]): "Most Played", classes=["card-title", "mb-0"], ), - classes=["card-header", "bg-white"], + classes=["card-header"], ), TopEpisodesTable( episodes=metrics["most_played"], metric_name="Plays", count_key="play_count", ), - classes=["card", "shadow-sm"], + classes=["card", "shadow-sm", "mb-3"], ), - classes=["col-lg-4"], + classes=["col-12"], ), html.div( html.div( @@ -288,16 +288,16 @@ class MetricsDashboard(Component[AnyChildren, MetricsAttrs]): "Most Downloaded", classes=["card-title", "mb-0"], ), - classes=["card-header", "bg-white"], + classes=["card-header"], ), TopEpisodesTable( episodes=metrics["most_downloaded"], metric_name="Downloads", count_key="download_count", ), - classes=["card", "shadow-sm"], + classes=["card", "shadow-sm", "mb-3"], ), - classes=["col-lg-4"], + classes=["col-12"], ), html.div( html.div( @@ -313,16 +313,16 @@ class MetricsDashboard(Component[AnyChildren, MetricsAttrs]): "Most Added to Feeds", classes=["card-title", "mb-0"], ), - classes=["card-header", "bg-white"], + classes=["card-header"], ), TopEpisodesTable( episodes=metrics["most_added"], metric_name="Adds", count_key="add_count", ), - classes=["card", "shadow-sm"], + classes=["card", "shadow-sm", "mb-3"], ), - classes=["col-lg-4"], + classes=["col-12"], ), classes=["row", "g-3"], ), -- cgit v1.2.3