solid_queue_tui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/exe/sqtui +6 -0
  4. data/lib/solid_queue_tui/actions/discard_job.rb +34 -0
  5. data/lib/solid_queue_tui/actions/discard_scheduled_job.rb +33 -0
  6. data/lib/solid_queue_tui/actions/dispatch_scheduled_job.rb +35 -0
  7. data/lib/solid_queue_tui/actions/retry_job.rb +76 -0
  8. data/lib/solid_queue_tui/application.rb +468 -0
  9. data/lib/solid_queue_tui/cli.rb +48 -0
  10. data/lib/solid_queue_tui/components/header.rb +105 -0
  11. data/lib/solid_queue_tui/components/help_bar.rb +77 -0
  12. data/lib/solid_queue_tui/components/job_table.rb +122 -0
  13. data/lib/solid_queue_tui/connection.rb +58 -0
  14. data/lib/solid_queue_tui/data/failed_query.rb +118 -0
  15. data/lib/solid_queue_tui/data/jobs_query.rb +178 -0
  16. data/lib/solid_queue_tui/data/processes_query.rb +75 -0
  17. data/lib/solid_queue_tui/data/queues_query.rb +36 -0
  18. data/lib/solid_queue_tui/data/stats.rb +65 -0
  19. data/lib/solid_queue_tui/dev_reloader.rb +53 -0
  20. data/lib/solid_queue_tui/version.rb +5 -0
  21. data/lib/solid_queue_tui/views/blocked_view.rb +121 -0
  22. data/lib/solid_queue_tui/views/dashboard_view.rb +187 -0
  23. data/lib/solid_queue_tui/views/failed_view.rb +298 -0
  24. data/lib/solid_queue_tui/views/finished_view.rb +216 -0
  25. data/lib/solid_queue_tui/views/in_progress_view.rb +114 -0
  26. data/lib/solid_queue_tui/views/job_detail_view.rb +236 -0
  27. data/lib/solid_queue_tui/views/processes_view.rb +142 -0
  28. data/lib/solid_queue_tui/views/queues_view.rb +96 -0
  29. data/lib/solid_queue_tui/views/scheduled_view.rb +215 -0
  30. data/lib/solid_queue_tui.rb +46 -0
  31. metadata +157 -0
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidQueueTui
4
+ module Views
5
+ class ScheduledView
6
+ def initialize(tui)
7
+ @tui = tui
8
+ @table_state = RatatuiRuby::TableState.new(nil)
9
+ @table_state.select(0)
10
+ @selected_row = 0
11
+ @jobs = []
12
+ @confirm_action = nil
13
+ end
14
+
15
+ def update(jobs:)
16
+ @jobs = jobs
17
+ @selected_row = @selected_row.clamp(0, [@jobs.size - 1, 0].max)
18
+ @table_state.select(@selected_row)
19
+ end
20
+
21
+ def render(frame, area)
22
+ if @confirm_action
23
+ content_area, confirm_area = @tui.layout_split(
24
+ area,
25
+ direction: :vertical,
26
+ constraints: [
27
+ @tui.constraint_fill(1),
28
+ @tui.constraint_length(3)
29
+ ]
30
+ )
31
+ render_table(frame, content_area)
32
+ render_confirm(frame, confirm_area)
33
+ else
34
+ render_table(frame, area)
35
+ end
36
+ end
37
+
38
+ def handle_input(event)
39
+ if @confirm_action
40
+ handle_confirm_input(event)
41
+ else
42
+ handle_normal_input(event)
43
+ end
44
+ end
45
+
46
+ def selected_item
47
+ return nil if @jobs.empty? || @selected_row >= @jobs.size
48
+ @jobs[@selected_row]
49
+ end
50
+
51
+ def capturing_input?
52
+ !!@confirm_action
53
+ end
54
+
55
+ def bindings
56
+ if @confirm_action
57
+ [
58
+ { key: "y", action: "Confirm" },
59
+ { key: "n/Esc", action: "Cancel" }
60
+ ]
61
+ else
62
+ [
63
+ { key: "j/k", action: "Navigate" },
64
+ { key: "Enter", action: "Detail" },
65
+ { key: "N", action: "Run Now" },
66
+ { key: "D", action: "Discard" },
67
+ { key: "G/g", action: "Bottom/Top" }
68
+ ]
69
+ end
70
+ end
71
+
72
+ def breadcrumb = "scheduled"
73
+
74
+ private
75
+
76
+ def handle_normal_input(event)
77
+ case event
78
+ in { type: :key, code: "j" } | { type: :key, code: "up" }
79
+ move_selection(-1)
80
+ in { type: :key, code: "k" } | { type: :key, code: "down" }
81
+ move_selection(1)
82
+ in { type: :key, code: "g" }
83
+ jump_to_top
84
+ in { type: :key, code: "G" }
85
+ jump_to_bottom
86
+ in { type: :key, code: "N" }
87
+ @confirm_action = :run_now if selected_item
88
+ nil
89
+ in { type: :key, code: "D" }
90
+ @confirm_action = :discard if selected_item
91
+ nil
92
+ else
93
+ nil
94
+ end
95
+ end
96
+
97
+ def handle_confirm_input(event)
98
+ case event
99
+ in { type: :key, code: "y" }
100
+ action = @confirm_action
101
+ @confirm_action = nil
102
+ case action
103
+ when :run_now
104
+ item = selected_item
105
+ return nil unless item
106
+ Actions::DispatchScheduledJob.call(item.id)
107
+ :refresh
108
+ when :discard
109
+ item = selected_item
110
+ return nil unless item
111
+ Actions::DiscardScheduledJob.call(item.id)
112
+ :refresh
113
+ end
114
+ in { type: :key, code: "n" } | { type: :key, code: "esc" }
115
+ @confirm_action = nil
116
+ nil
117
+ else
118
+ nil
119
+ end
120
+ end
121
+
122
+ def move_selection(delta)
123
+ return if @jobs.empty?
124
+ @selected_row = (@selected_row + delta).clamp(0, @jobs.size - 1)
125
+ @table_state.select(@selected_row)
126
+ end
127
+
128
+ def jump_to_top
129
+ @selected_row = 0
130
+ @table_state.select(0)
131
+ end
132
+
133
+ def jump_to_bottom
134
+ return if @jobs.empty?
135
+ @selected_row = @jobs.size - 1
136
+ @table_state.select(@selected_row)
137
+ end
138
+
139
+ def render_table(frame, area)
140
+ columns = [
141
+ { key: :id, label: "ID", width: 8 },
142
+ { key: :class_name, label: "JOB CLASS", width: :fill },
143
+ { key: :queue_name, label: "QUEUE", width: 14 },
144
+ { key: :priority, label: "PRI", width: 5 },
145
+ { key: :scheduled_at, label: "SCHEDULED AT", width: 20 },
146
+ { key: :created_at, label: "CREATED", width: 12 }
147
+ ]
148
+
149
+ rows = @jobs.map do |job|
150
+ {
151
+ id: job.id,
152
+ class_name: job.class_name,
153
+ queue_name: job.queue_name,
154
+ priority: job.priority,
155
+ scheduled_at: format_time(job.scheduled_at),
156
+ created_at: time_ago(job.created_at)
157
+ }
158
+ end
159
+
160
+ table = Components::JobTable.new(
161
+ @tui,
162
+ title: "Scheduled Jobs",
163
+ columns: columns,
164
+ rows: rows,
165
+ selected_row: @selected_row,
166
+ empty_message: "No scheduled jobs"
167
+ )
168
+
169
+ table.render(frame, area, @table_state)
170
+ end
171
+
172
+ def render_confirm(frame, area)
173
+ message = case @confirm_action
174
+ when :run_now
175
+ job = selected_item
176
+ "Run job ##{job&.id} (#{job&.class_name}) now? [y/n]"
177
+ when :discard
178
+ job = selected_item
179
+ "Discard job ##{job&.id} (#{job&.class_name})? This cannot be undone. [y/n]"
180
+ end
181
+
182
+ frame.render_widget(
183
+ @tui.paragraph(
184
+ text: " #{message}",
185
+ style: @tui.style(fg: :yellow, modifiers: [:bold]),
186
+ block: @tui.block(
187
+ title: " Confirm ",
188
+ title_style: @tui.style(fg: :red, modifiers: [:bold]),
189
+ borders: [:all],
190
+ border_type: :rounded,
191
+ border_style: @tui.style(fg: :red)
192
+ )
193
+ ),
194
+ area
195
+ )
196
+ end
197
+
198
+ def format_time(time)
199
+ return "n/a" unless time
200
+ time.strftime("%Y-%m-%d %H:%M:%S")
201
+ end
202
+
203
+ def time_ago(time)
204
+ return "n/a" unless time
205
+ seconds = (Time.now.utc - time).to_i
206
+ case seconds
207
+ when 0..59 then "#{seconds}s ago"
208
+ when 60..3599 then "#{seconds / 60}m ago"
209
+ when 3600..86399 then "#{seconds / 3600}h ago"
210
+ else "#{seconds / 86400}d ago"
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "time"
5
+
6
+ require_relative "solid_queue_tui/version"
7
+ require_relative "solid_queue_tui/connection"
8
+
9
+ # Data layer
10
+ require_relative "solid_queue_tui/data/stats"
11
+ require_relative "solid_queue_tui/data/jobs_query"
12
+ require_relative "solid_queue_tui/data/queues_query"
13
+ require_relative "solid_queue_tui/data/processes_query"
14
+ require_relative "solid_queue_tui/data/failed_query"
15
+
16
+ # Actions
17
+ require_relative "solid_queue_tui/actions/retry_job"
18
+ require_relative "solid_queue_tui/actions/discard_job"
19
+ require_relative "solid_queue_tui/actions/dispatch_scheduled_job"
20
+ require_relative "solid_queue_tui/actions/discard_scheduled_job"
21
+
22
+ # Components
23
+ require_relative "solid_queue_tui/components/header"
24
+ require_relative "solid_queue_tui/components/job_table"
25
+ require_relative "solid_queue_tui/components/help_bar"
26
+
27
+ # Views
28
+ require_relative "solid_queue_tui/views/dashboard_view"
29
+ require_relative "solid_queue_tui/views/queues_view"
30
+ require_relative "solid_queue_tui/views/failed_view"
31
+ require_relative "solid_queue_tui/views/in_progress_view"
32
+ require_relative "solid_queue_tui/views/blocked_view"
33
+ require_relative "solid_queue_tui/views/scheduled_view"
34
+ require_relative "solid_queue_tui/views/finished_view"
35
+ require_relative "solid_queue_tui/views/processes_view"
36
+ require_relative "solid_queue_tui/views/job_detail_view"
37
+
38
+ # Dev tools
39
+ require_relative "solid_queue_tui/dev_reloader"
40
+
41
+ # Application
42
+ require_relative "solid_queue_tui/application"
43
+ require_relative "solid_queue_tui/cli"
44
+
45
+ module SolidQueueTui
46
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solid_queue_tui
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Shiva Reddy
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ratatui_ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '7.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '7.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pg
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '13.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '13.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '5.0'
97
+ description: Real-time terminal dashboard to monitor and manage Solid Queue jobs.
98
+ Built with ratatui_ruby for native Rust rendering performance.
99
+ email:
100
+ executables:
101
+ - sqtui
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - LICENSE.txt
106
+ - exe/sqtui
107
+ - lib/solid_queue_tui.rb
108
+ - lib/solid_queue_tui/actions/discard_job.rb
109
+ - lib/solid_queue_tui/actions/discard_scheduled_job.rb
110
+ - lib/solid_queue_tui/actions/dispatch_scheduled_job.rb
111
+ - lib/solid_queue_tui/actions/retry_job.rb
112
+ - lib/solid_queue_tui/application.rb
113
+ - lib/solid_queue_tui/cli.rb
114
+ - lib/solid_queue_tui/components/header.rb
115
+ - lib/solid_queue_tui/components/help_bar.rb
116
+ - lib/solid_queue_tui/components/job_table.rb
117
+ - lib/solid_queue_tui/connection.rb
118
+ - lib/solid_queue_tui/data/failed_query.rb
119
+ - lib/solid_queue_tui/data/jobs_query.rb
120
+ - lib/solid_queue_tui/data/processes_query.rb
121
+ - lib/solid_queue_tui/data/queues_query.rb
122
+ - lib/solid_queue_tui/data/stats.rb
123
+ - lib/solid_queue_tui/dev_reloader.rb
124
+ - lib/solid_queue_tui/version.rb
125
+ - lib/solid_queue_tui/views/blocked_view.rb
126
+ - lib/solid_queue_tui/views/dashboard_view.rb
127
+ - lib/solid_queue_tui/views/failed_view.rb
128
+ - lib/solid_queue_tui/views/finished_view.rb
129
+ - lib/solid_queue_tui/views/in_progress_view.rb
130
+ - lib/solid_queue_tui/views/job_detail_view.rb
131
+ - lib/solid_queue_tui/views/processes_view.rb
132
+ - lib/solid_queue_tui/views/queues_view.rb
133
+ - lib/solid_queue_tui/views/scheduled_view.rb
134
+ homepage: https://github.com/shivareddyaluri/solid-queue-tui
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: 3.2.0
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubygems_version: 3.5.11
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: A K9s-inspired terminal UI for Solid Queue
157
+ test_files: []