From f8217a634dbfe5dbe215e15a500d7b2d2c57f1e2 Mon Sep 17 00:00:00 2001 From: Chuck Smith Date: Tue, 26 May 2026 10:36:32 -0400 Subject: [PATCH] feat: Solid Cable channel browser Adds a channel list to GET /cable showing per-channel message count and last-message time, ordered by most recent activity; fixes SQLite datetime string-casting via pluck+map; includes empty state; adds ActiveSupport::Testing::TimeHelpers to RSpec config for travel helper. Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 4 +++ .../solid_stack_web/cable_controller.rb | 15 +++++++++- .../solid_stack_web/application.html.erb | 9 ++++++ .../solid_stack_web/cable/index.html.erb | 16 ++++++++-- spec/rails_helper.rb | 1 + spec/requests/solid_stack_web/cable_spec.rb | 29 ++++++++++++++++++- 6 files changed, 70 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fe8696..441b17b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Solid Cable channel browser — `GET /cable` lists all distinct channels with per-channel message count and last-message timestamp, ordered by most recent activity; a total-messages stat and channel-count stat are shown at the top; empty state shown when no messages exist; Cable subnav (Overview) added to the layout + ## [0.5.0] - 2026-05-26 ### Added diff --git a/app/controllers/solid_stack_web/cable_controller.rb b/app/controllers/solid_stack_web/cable_controller.rb index 722f362..bbda29a 100644 --- a/app/controllers/solid_stack_web/cable_controller.rb +++ b/app/controllers/solid_stack_web/cable_controller.rb @@ -2,7 +2,20 @@ module SolidStackWeb class CableController < ApplicationController def index @total_messages = ::SolidCable::Message.count - @channels = ::SolidCable::Message.distinct.pluck(:channel).sort + @channels = channel_rows + end + + private + + def channel_rows + ::SolidCable::Message + .group(:channel) + .order(Arel.sql("MAX(created_at) DESC")) + .pluck(:channel, Arel.sql("COUNT(*)"), Arel.sql("MAX(created_at)")) + .map do |ch, cnt, last_at| + last_at = Time.zone.parse(last_at) if last_at.is_a?(String) + { channel: ch, message_count: cnt, last_message_at: last_at } + end end end end diff --git a/app/views/layouts/solid_stack_web/application.html.erb b/app/views/layouts/solid_stack_web/application.html.erb index 462f69a..141079e 100644 --- a/app/views/layouts/solid_stack_web/application.html.erb +++ b/app/views/layouts/solid_stack_web/application.html.erb @@ -35,6 +35,15 @@ <% end %> + <% if current_section == :cable %> + + <% end %> + <% if current_section == :queue %>