ragdoll-rails 0.1.9 → 0.1.11

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/ragdoll/application.js +129 -0
  3. data/app/assets/javascripts/ragdoll/bulk_upload_status.js +454 -0
  4. data/app/assets/stylesheets/ragdoll/application.css +84 -0
  5. data/app/assets/stylesheets/ragdoll/bulk_upload_status.css +379 -0
  6. data/app/channels/application_cable/channel.rb +6 -0
  7. data/app/channels/application_cable/connection.rb +6 -0
  8. data/app/channels/ragdoll/bulk_upload_status_channel.rb +27 -0
  9. data/app/channels/ragdoll/file_processing_channel.rb +26 -0
  10. data/app/components/ragdoll/alert_component.html.erb +4 -0
  11. data/app/components/ragdoll/alert_component.rb +32 -0
  12. data/app/components/ragdoll/application_component.rb +6 -0
  13. data/app/components/ragdoll/card_component.html.erb +15 -0
  14. data/app/components/ragdoll/card_component.rb +21 -0
  15. data/app/components/ragdoll/document_list_component.html.erb +41 -0
  16. data/app/components/ragdoll/document_list_component.rb +13 -0
  17. data/app/components/ragdoll/document_table_component.html.erb +76 -0
  18. data/app/components/ragdoll/document_table_component.rb +13 -0
  19. data/app/components/ragdoll/empty_state_component.html.erb +12 -0
  20. data/app/components/ragdoll/empty_state_component.rb +17 -0
  21. data/app/components/ragdoll/flash_messages_component.html.erb +3 -0
  22. data/app/components/ragdoll/flash_messages_component.rb +37 -0
  23. data/app/components/ragdoll/navbar_component.html.erb +24 -0
  24. data/app/components/ragdoll/navbar_component.rb +31 -0
  25. data/app/components/ragdoll/page_header_component.html.erb +13 -0
  26. data/app/components/ragdoll/page_header_component.rb +15 -0
  27. data/app/components/ragdoll/stats_card_component.html.erb +11 -0
  28. data/app/components/ragdoll/stats_card_component.rb +17 -0
  29. data/app/components/ragdoll/status_badge_component.html.erb +3 -0
  30. data/app/components/ragdoll/status_badge_component.rb +30 -0
  31. data/app/controllers/ragdoll/api/v1/analytics_controller.rb +72 -0
  32. data/app/controllers/ragdoll/api/v1/base_controller.rb +29 -0
  33. data/app/controllers/ragdoll/api/v1/documents_controller.rb +148 -0
  34. data/app/controllers/ragdoll/api/v1/search_controller.rb +87 -0
  35. data/app/controllers/ragdoll/api/v1/system_controller.rb +97 -0
  36. data/app/controllers/ragdoll/application_controller.rb +17 -0
  37. data/app/controllers/ragdoll/configuration_controller.rb +82 -0
  38. data/app/controllers/ragdoll/dashboard_controller.rb +98 -0
  39. data/app/controllers/ragdoll/documents_controller.rb +460 -0
  40. data/app/controllers/ragdoll/documents_controller_backup.rb +68 -0
  41. data/app/controllers/ragdoll/jobs_controller.rb +116 -0
  42. data/app/controllers/ragdoll/search_controller.rb +368 -0
  43. data/app/jobs/application_job.rb +9 -0
  44. data/app/jobs/ragdoll/bulk_document_processing_job.rb +280 -0
  45. data/app/jobs/ragdoll/process_file_job.rb +166 -0
  46. data/app/services/ragdoll/worker_health_service.rb +111 -0
  47. data/app/views/layouts/ragdoll/application.html.erb +162 -0
  48. data/app/views/ragdoll/dashboard/analytics.html.erb +333 -0
  49. data/app/views/ragdoll/dashboard/index.html.erb +208 -0
  50. data/app/views/ragdoll/documents/edit.html.erb +91 -0
  51. data/app/views/ragdoll/documents/index.html.erb +302 -0
  52. data/app/views/ragdoll/documents/new.html.erb +1518 -0
  53. data/app/views/ragdoll/documents/show.html.erb +188 -0
  54. data/app/views/ragdoll/documents/upload_results.html.erb +248 -0
  55. data/app/views/ragdoll/jobs/index.html.erb +669 -0
  56. data/app/views/ragdoll/jobs/show.html.erb +129 -0
  57. data/app/views/ragdoll/search/index.html.erb +324 -0
  58. data/config/cable.yml +12 -0
  59. data/config/routes.rb +56 -1
  60. data/lib/ragdoll/rails/engine.rb +32 -1
  61. data/lib/ragdoll/rails/version.rb +1 -1
  62. metadata +86 -1
@@ -0,0 +1,129 @@
1
+ <% content_for(:title, "Job Details") %>
2
+
3
+ <%= render Ragdoll::PageHeaderComponent.new(
4
+ title: "Job Details",
5
+ subtitle: "View job information and execution details",
6
+ icon: "fas fa-info-circle"
7
+ ) %>
8
+
9
+ <div class="row">
10
+ <div class="col-md-8">
11
+ <div class="card">
12
+ <div class="card-header">
13
+ <h5 class="card-title mb-0">
14
+ <i class="fas fa-cog"></i> Job Information
15
+ </h5>
16
+ </div>
17
+ <div class="card-body">
18
+ <dl class="row">
19
+ <dt class="col-sm-3">Job ID:</dt>
20
+ <dd class="col-sm-9"><%= @job.id %></dd>
21
+
22
+ <dt class="col-sm-3">Class:</dt>
23
+ <dd class="col-sm-9"><code><%= @job.class_name %></code></dd>
24
+
25
+ <dt class="col-sm-3">Status:</dt>
26
+ <dd class="col-sm-9">
27
+ <% if @job.finished_at %>
28
+ <%= render Ragdoll::StatusBadgeComponent.new(status: "completed") %>
29
+ <% else %>
30
+ <%= render Ragdoll::StatusBadgeComponent.new(status: "pending") %>
31
+ <% end %>
32
+ </dd>
33
+
34
+ <dt class="col-sm-3">Priority:</dt>
35
+ <dd class="col-sm-9"><%= @job.priority %></dd>
36
+
37
+ <dt class="col-sm-3">Queue:</dt>
38
+ <dd class="col-sm-9"><%= @job.queue_name %></dd>
39
+
40
+ <dt class="col-sm-3">Created:</dt>
41
+ <dd class="col-sm-9">
42
+ <%= @job.created_at.strftime("%B %d, %Y at %I:%M %p") %>
43
+ <small class="text-muted">(<%= time_ago_in_words(@job.created_at) %> ago)</small>
44
+ </dd>
45
+
46
+ <% if @job.scheduled_at %>
47
+ <dt class="col-sm-3">Scheduled For:</dt>
48
+ <dd class="col-sm-9">
49
+ <%= @job.scheduled_at.strftime("%B %d, %Y at %I:%M %p") %>
50
+ <% if @job.scheduled_at > Time.current %>
51
+ <small class="text-muted">(in <%= time_ago_in_words(@job.scheduled_at) %>)</small>
52
+ <% end %>
53
+ </dd>
54
+ <% end %>
55
+
56
+ <% if @job.finished_at %>
57
+ <dt class="col-sm-3">Finished:</dt>
58
+ <dd class="col-sm-9">
59
+ <%= @job.finished_at.strftime("%B %d, %Y at %I:%M %p") %>
60
+ <small class="text-muted">(<%= time_ago_in_words(@job.finished_at) %> ago)</small>
61
+ </dd>
62
+
63
+ <dt class="col-sm-3">Duration:</dt>
64
+ <dd class="col-sm-9">
65
+ <%= distance_of_time_in_words(@job.created_at, @job.finished_at) %>
66
+ </dd>
67
+ <% end %>
68
+ </dl>
69
+ </div>
70
+ </div>
71
+
72
+ <% if @job.arguments.present? %>
73
+ <div class="card mt-4">
74
+ <div class="card-header">
75
+ <h5 class="card-title mb-0">
76
+ <i class="fas fa-list"></i> Arguments
77
+ </h5>
78
+ </div>
79
+ <div class="card-body">
80
+ <pre class="bg-light p-3 rounded"><code><%= JSON.pretty_generate(@job.arguments) %></code></pre>
81
+ </div>
82
+ </div>
83
+ <% end %>
84
+ </div>
85
+
86
+ <div class="col-md-4">
87
+ <div class="card">
88
+ <div class="card-header">
89
+ <h5 class="card-title mb-0">
90
+ <i class="fas fa-tools"></i> Actions
91
+ </h5>
92
+ </div>
93
+ <div class="card-body">
94
+ <div class="d-grid gap-2">
95
+ <%= link_to ragdoll.jobs_path, class: "btn btn-outline-secondary" do %>
96
+ <i class="fas fa-arrow-left"></i> Back to Jobs
97
+ <% end %>
98
+
99
+ <% unless @job.finished_at %>
100
+ <%= link_to ragdoll.job_path(@job), method: :delete,
101
+ class: "btn btn-outline-danger",
102
+ data: { confirm: "Are you sure you want to delete this job?" } do %>
103
+ <i class="fas fa-trash"></i> Delete Job
104
+ <% end %>
105
+ <% end %>
106
+ </div>
107
+ </div>
108
+ </div>
109
+
110
+ <div class="card mt-4">
111
+ <div class="card-header">
112
+ <h5 class="card-title mb-0">
113
+ <i class="fas fa-info"></i> Job Stats
114
+ </h5>
115
+ </div>
116
+ <div class="card-body">
117
+ <small class="text-muted">
118
+ <% if @job.finished_at %>
119
+ This job completed successfully.
120
+ <% elsif @job.scheduled_at && @job.scheduled_at > Time.current %>
121
+ This job is scheduled to run in the future.
122
+ <% else %>
123
+ This job is waiting to be processed.
124
+ <% end %>
125
+ </small>
126
+ </div>
127
+ </div>
128
+ </div>
129
+ </div>
@@ -0,0 +1,324 @@
1
+ <% content_for :title, "Search - Ragdoll Engine" %>
2
+
3
+ <div class="row">
4
+ <div class="col-12">
5
+ <h1><i class="fas fa-search"></i> Search Documents</h1>
6
+ <p class="text-muted">Use semantic search to find relevant content across all your documents</p>
7
+
8
+ <% if @reconstructed_search %>
9
+ <div class="alert alert-info">
10
+ <i class="fas fa-history me-2"></i>
11
+ <strong>Previous Search Loaded:</strong> All search parameters have been restored from your search history.
12
+ <small class="d-block mt-1">
13
+ Original search from <%= time_ago_in_words(@reconstructed_search.created_at) %> ago
14
+ • <%= @reconstructed_search.search_type.titleize %> search
15
+ • <%= @reconstructed_search.results_count %> results found
16
+ </small>
17
+ </div>
18
+ <% end %>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="row">
23
+ <div class="col-md-8">
24
+ <div class="card">
25
+ <div class="card-header">
26
+ <h5><i class="fas fa-search"></i> Search Query</h5>
27
+ </div>
28
+ <div class="card-body">
29
+ <%= form_with url: ragdoll.search_path, method: :post, local: true, class: "search-form", data: { turbo: false } do |form| %>
30
+ <div class="input-group mb-3">
31
+ <%= form.text_field :query, placeholder: "Enter your search query...", value: @query, class: "form-control form-control-lg", required: true %>
32
+ <div class="input-group-append">
33
+ <%= form.submit "Search", class: "btn btn-primary btn-lg" %>
34
+ </div>
35
+ </div>
36
+
37
+ <div class="row">
38
+ <div class="col-md-6">
39
+ <div class="mb-3">
40
+ <label class="form-label">Document Type</label>
41
+ <%= form.select :document_type,
42
+ options_for_select([["All Types", ""]] + ::Ragdoll::Document.distinct.pluck(:document_type).compact.map { |type| [type.titleize, type] }, @filters[:document_type]),
43
+ {},
44
+ { class: "form-select" } %>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="col-md-6">
49
+ <div class="mb-3">
50
+ <label class="form-label">Status</label>
51
+ <%= form.select :status,
52
+ options_for_select([["All Statuses", ""], ["Processed", "processed"], ["Failed", "failed"], ["Pending", "pending"]], @filters[:status]),
53
+ {},
54
+ { class: "form-select" } %>
55
+ </div>
56
+ </div>
57
+ </div>
58
+
59
+ <div class="row">
60
+ <div class="col-md-3">
61
+ <div class="mb-3">
62
+ <label class="form-label">Search Types</label>
63
+ <div class="form-check">
64
+ <%= form.check_box :use_similarity_search, { checked: @use_similarity_search != 'false', class: "form-check-input" }, "true", "false" %>
65
+ <%= form.label :use_similarity_search, "Similarity Search", class: "form-check-label" %>
66
+ <small class="form-text text-muted d-block">AI-powered semantic search</small>
67
+ </div>
68
+ <div class="form-check mt-2">
69
+ <%= form.check_box :use_fulltext_search, { checked: @use_fulltext_search != 'false', class: "form-check-input" }, "true", "false" %>
70
+ <%= form.label :use_fulltext_search, "Full Text Search", class: "form-check-label" %>
71
+ <small class="form-text text-muted d-block">Traditional keyword matching</small>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <div class="col-md-3">
77
+ <div class="mb-3">
78
+ <label class="form-label">Max Results</label>
79
+ <%= form.number_field :limit, value: @filters[:limit], min: 1, max: 50, class: "form-control" %>
80
+ </div>
81
+ </div>
82
+
83
+ <div class="col-md-3">
84
+ <div class="mb-3">
85
+ <label class="form-label">Similarity Threshold</label>
86
+ <%= form.number_field :threshold, value: @filters[:threshold], min: 0.001, max: 1.0, step: 0.001, class: "form-control" %>
87
+ </div>
88
+ </div>
89
+
90
+ <div class="col-md-3">
91
+ <div class="mb-3">
92
+ <label class="form-label">Additional Options</label>
93
+ <div class="form-check">
94
+ <%= form.check_box :use_usage_ranking, { checked: params[:use_usage_ranking] == 'true', class: "form-check-input" }, "true", "false" %>
95
+ <%= form.label :use_usage_ranking, "Use Usage Ranking", class: "form-check-label" %>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ <% end %>
101
+ </div>
102
+ </div>
103
+
104
+ <% if @search_performed %>
105
+ <div class="card mt-4">
106
+ <div class="card-header">
107
+ <h5><i class="fas fa-list"></i> Search Results</h5>
108
+ <% if @detailed_results %>
109
+ <span class="badge bg-primary"><%= @detailed_results.count %> results</span>
110
+ <% end %>
111
+ </div>
112
+ <div class="card-body">
113
+ <% if @error %>
114
+ <div class="alert alert-danger">
115
+ <i class="fas fa-exclamation-circle"></i> Error: <%= @error %>
116
+ </div>
117
+ <% elsif @detailed_results && @detailed_results.any? %>
118
+ <% @detailed_results.each_with_index do |result, index| %>
119
+ <div class="card mb-3">
120
+ <div class="card-body">
121
+ <div class="d-flex justify-content-between align-items-start">
122
+ <div class="flex-grow-1">
123
+ <h6 class="card-title">
124
+ <%= link_to result[:document].title, ragdoll.document_path(result[:document]), class: "text-decoration-none" %>
125
+ <span class="badge bg-secondary ms-2"><%= result[:document].document_type&.upcase %></span>
126
+ <% if result[:search_type] == 'similarity' %>
127
+ <span class="badge bg-info ms-1" title="Found via AI-powered similarity search">
128
+ <i class="fas fa-brain"></i> Similarity
129
+ </span>
130
+ <% elsif result[:search_type] == 'fulltext' %>
131
+ <span class="badge bg-success ms-1" title="Found via full-text keyword search">
132
+ <i class="fas fa-font"></i> Full Text
133
+ </span>
134
+ <% end %>
135
+ </h6>
136
+ <p class="card-text"><%= result[:content] %></p>
137
+ <div class="d-flex align-items-center">
138
+ <small class="text-muted">
139
+ <% if result[:embedding] %>
140
+ Chunk <%= result[:embedding].chunk_index %>
141
+ <% else %>
142
+ Document summary
143
+ <% end %>
144
+ </small>
145
+ <% if result[:usage_count] && result[:usage_count] > 0 %>
146
+ <span class="badge bg-success ms-2"><%= result[:usage_count] %> uses</span>
147
+ <% end %>
148
+ <% if result[:last_used] %>
149
+ <small class="text-muted ms-2">
150
+ Last used: <%= time_ago_in_words(result[:last_used]) %> ago
151
+ </small>
152
+ <% end %>
153
+ </div>
154
+ </div>
155
+ <div class="text-end">
156
+ <% if result[:similarity] %>
157
+ <div class="similarity-score">
158
+ <span class="badge bg-info">
159
+ <%= number_with_precision(result[:similarity], precision: 3) %>
160
+ </span>
161
+ <br>
162
+ <small class="text-muted">similarity</small>
163
+ </div>
164
+ <% else %>
165
+ <div class="text-muted">
166
+ <i class="fas fa-font"></i><br>
167
+ <small>Keyword Match</small>
168
+ </div>
169
+ <% end %>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ <% end %>
175
+ <% else %>
176
+ <div class="text-center py-4">
177
+ <i class="fas fa-search fa-3x text-muted mb-3"></i>
178
+ <h5>No results found</h5>
179
+ <p class="text-muted">Try adjusting your search query or filters.</p>
180
+
181
+ <% if @below_threshold_stats && @below_threshold_stats[:highest] %>
182
+ <div class="alert alert-warning mt-3">
183
+ <h6><i class="fas fa-exclamation-triangle"></i>
184
+ <% if @below_threshold_stats[:count] && @below_threshold_stats[:count] > 0 %>
185
+ Results exist below your threshold!
186
+ <% else %>
187
+ All results are below your threshold!
188
+ <% end %>
189
+ </h6>
190
+ <p class="mb-3">
191
+ <% if @below_threshold_stats[:count] && @below_threshold_stats[:count] > 0 %>
192
+ Found <strong><%= @below_threshold_stats[:count] %></strong> potential matches below your threshold of
193
+ <% else %>
194
+ All matches found are below your threshold of
195
+ <% end %>
196
+ <strong><%= number_with_precision(@similarity_threshold_used, precision: 3) %></strong>
197
+ <% search_type = params[:use_similarity_search] != 'false' && params[:use_fulltext_search] != 'false' ? 'hybrid' :
198
+ params[:use_similarity_search] != 'false' ? 'semantic' : 'fulltext' %>
199
+ <small class="text-muted d-block">(<%= search_type %> search)</small>
200
+ </p>
201
+
202
+ <div class="row text-center mb-3">
203
+ <div class="col-md-4">
204
+ <strong>Highest Match</strong><br>
205
+ <span class="badge bg-success fs-6"><%= number_with_precision(@below_threshold_stats[:highest], precision: 3) %></span>
206
+ </div>
207
+ <div class="col-md-4">
208
+ <strong>Average Match</strong><br>
209
+ <span class="badge bg-info fs-6"><%= number_with_precision(@below_threshold_stats[:average], precision: 3) %></span>
210
+ </div>
211
+ <div class="col-md-4">
212
+ <strong>Lowest Match</strong><br>
213
+ <span class="badge bg-secondary fs-6"><%= number_with_precision(@below_threshold_stats[:lowest], precision: 3) %></span>
214
+ </div>
215
+ </div>
216
+
217
+ <% if @below_threshold_stats[:suggested_threshold] && @below_threshold_stats[:suggested_threshold] > 0 %>
218
+ <div class="d-grid gap-2">
219
+ <%= form_with url: ragdoll.search_path, method: :post, local: true, data: { turbo: false } do |form| %>
220
+ <%= form.hidden_field :query, value: @query %>
221
+ <%= form.hidden_field :threshold, value: @below_threshold_stats[:suggested_threshold] %>
222
+ <%= form.hidden_field :document_type, value: @filters[:document_type] %>
223
+ <%= form.hidden_field :status, value: @filters[:status] %>
224
+ <%= form.hidden_field :limit, value: @filters[:limit] %>
225
+ <%= form.hidden_field :use_similarity_search, value: params[:use_similarity_search] %>
226
+ <%= form.hidden_field :use_fulltext_search, value: params[:use_fulltext_search] %>
227
+ <%= form.button type: :submit, class: "btn btn-warning w-100" do %>
228
+ <i class="fas fa-redo"></i>
229
+ Search again with threshold: <%= number_with_precision(@below_threshold_stats[:suggested_threshold], precision: 3) %>
230
+ <% end %>
231
+ <% end %>
232
+ </div>
233
+ <% end %>
234
+ </div>
235
+ <% elsif @similarity_search_attempted && @similarity_stats && @similarity_stats[:total_similarities_calculated] && @similarity_stats[:total_similarities_calculated] > 0 %>
236
+ <div class="alert alert-info mt-3">
237
+ <h6><i class="fas fa-chart-line"></i> Similarity Search Statistics</h6>
238
+ <p class="mb-2">No results exceeded the similarity threshold of <strong><%= number_with_precision(@similarity_threshold_used, precision: 3) %></strong></p>
239
+ <div class="row text-center">
240
+ <div class="col-md-4">
241
+ <strong>Highest Similarity</strong><br>
242
+ <span class="badge bg-success"><%= number_with_precision(@similarity_stats[:highest_similarity] || 0, precision: 3) %></span>
243
+ </div>
244
+ <div class="col-md-4">
245
+ <strong>Average Similarity</strong><br>
246
+ <span class="badge bg-info"><%= number_with_precision(@similarity_stats[:average_similarity] || 0, precision: 3) %></span>
247
+ </div>
248
+ <div class="col-md-4">
249
+ <strong>Lowest Similarity</strong><br>
250
+ <span class="badge bg-secondary"><%= number_with_precision(@similarity_stats[:lowest_similarity] || 0, precision: 3) %></span>
251
+ </div>
252
+ </div>
253
+ <small class="text-muted d-block mt-2">
254
+ Checked <%= @similarity_stats[:total_similarities_calculated] %> embeddings
255
+ <% if @similarity_stats[:similarities_above_threshold] && @similarity_stats[:similarities_above_threshold] > 0 %>
256
+ • <%= @similarity_stats[:similarities_above_threshold] %> were above threshold but filtered by other criteria
257
+ <% end %>
258
+ </small>
259
+ </div>
260
+ <% end %>
261
+ </div>
262
+ <% end %>
263
+ </div>
264
+ </div>
265
+ <% end %>
266
+ </div>
267
+
268
+ <div class="col-md-4">
269
+ <div class="card">
270
+ <div class="card-header">
271
+ <h5><i class="fas fa-info-circle"></i> Search Tips</h5>
272
+ </div>
273
+ <div class="card-body">
274
+ <h6>How to Search:</h6>
275
+ <ul class="list-unstyled">
276
+ <li><i class="fas fa-lightbulb text-warning"></i> Use natural language queries</li>
277
+ <li><i class="fas fa-quote-left text-info"></i> Ask questions or describe concepts</li>
278
+ <li><i class="fas fa-filter text-success"></i> Use filters to narrow results</li>
279
+ <li><i class="fas fa-sliders-h text-primary"></i> Adjust similarity threshold</li>
280
+ </ul>
281
+
282
+ <hr>
283
+
284
+ <h6>Search Features:</h6>
285
+ <ul class="list-unstyled">
286
+ <li><i class="fas fa-brain text-primary"></i> Semantic understanding</li>
287
+ <li><i class="fas fa-chart-line text-success"></i> Usage-based ranking</li>
288
+ <li><i class="fas fa-bullseye text-info"></i> Similarity scoring</li>
289
+ <li><i class="fas fa-clock text-warning"></i> Recency weighting</li>
290
+ </ul>
291
+ </div>
292
+ </div>
293
+
294
+ <div class="card mt-4">
295
+ <div class="card-header">
296
+ <h5><i class="fas fa-star"></i> Popular Queries</h5>
297
+ </div>
298
+ <div class="card-body">
299
+ <% if @popular_queries&.any? %>
300
+ <% @popular_queries.each do |query, count| %>
301
+ <div class="d-flex justify-content-between align-items-center mb-2">
302
+ <%= form_with url: ragdoll.search_path, method: :post, local: true, class: "d-inline", data: { turbo: false } do |form| %>
303
+ <%= form.hidden_field :query, value: query %>
304
+ <%= form.hidden_field :threshold, value: (::Rails.env.development? ? 0.001 : 0.7) %>
305
+ <%= form.hidden_field :document_type, value: '' %>
306
+ <%= form.hidden_field :status, value: '' %>
307
+ <%= form.hidden_field :limit, value: 10 %>
308
+ <%= form.hidden_field :use_similarity_search, value: 'true' %>
309
+ <%= form.hidden_field :use_fulltext_search, value: 'true' %>
310
+ <%= form.button type: :submit, class: "btn btn-link p-0 text-decoration-none border-0 text-start", style: "font: inherit;" do %>
311
+ <%= query %>
312
+ <% end %>
313
+ <% end %>
314
+ <span class="badge bg-secondary"><%= count %></span>
315
+ </div>
316
+ <% end %>
317
+ <% else %>
318
+ <p class="text-muted">No popular queries yet.</p>
319
+ <% end %>
320
+ </div>
321
+ </div>
322
+
323
+ </div>
324
+ </div>
data/config/cable.yml ADDED
@@ -0,0 +1,12 @@
1
+ development:
2
+ # Use Redis adapter for reliable cross-process communication
3
+ adapter: redis
4
+ url: redis://localhost:6379/0
5
+
6
+ test:
7
+ adapter: test
8
+
9
+ production:
10
+ adapter: redis
11
+ url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
12
+ channel_prefix: ragdoll_rails_production
data/config/routes.rb CHANGED
@@ -1,5 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Ragdoll::Rails::Engine.routes.draw do
4
- # Define your engine routes here
4
+ # Dashboard and Analytics
5
+ get "/" => "dashboard#index", as: :root
6
+ get "dashboard" => "dashboard#index", as: :dashboard_index
7
+ get "analytics" => "dashboard#analytics", as: :analytics
8
+
9
+ # Job Queue Dashboard
10
+ resources :jobs, only: [:index, :show, :destroy] do
11
+ member do
12
+ post :retry
13
+ end
14
+ collection do
15
+ get :health
16
+ post :restart_workers
17
+ post :bulk_delete
18
+ post :bulk_retry
19
+ delete :cancel_all_pending
20
+ end
21
+ end
22
+
23
+ # Document Management
24
+ resources :documents do
25
+ member do
26
+ get :preview
27
+ post :reprocess
28
+ get :download
29
+ end
30
+ collection do
31
+ post :bulk_upload
32
+ post :bulk_delete
33
+ post :bulk_reprocess
34
+ get :status
35
+ post :upload_async
36
+ end
37
+ end
38
+
39
+ # Search Interface
40
+ get "search" => "search#index", as: :search_index
41
+ post "search" => "search#search", as: :search
42
+
43
+ # Configuration
44
+ get "configuration" => "configuration#index", as: :configuration
45
+ patch "configuration" => "configuration#update"
46
+
47
+ # API endpoints for AJAX interactions
48
+ namespace :api do
49
+ namespace :v1 do
50
+ resources :documents, only: [:index, :show, :create, :update, :destroy] do
51
+ member do
52
+ post :reprocess
53
+ end
54
+ end
55
+ post "search" => "search#search"
56
+ get "analytics" => "analytics#index"
57
+ get "system_stats" => "system#stats"
58
+ end
59
+ end
5
60
  end
@@ -5,7 +5,7 @@ require 'rails/engine'
5
5
  module Ragdoll
6
6
  module Rails
7
7
  class Engine < ::Rails::Engine
8
- isolate_namespace Ragdoll::Rails
8
+ isolate_namespace Ragdoll
9
9
  engine_name 'ragdoll'
10
10
 
11
11
  # Configure the engine to use migrations from the ragdoll gem
@@ -34,8 +34,39 @@ module Ragdoll
34
34
  initializer "ragdoll.configure" do |app|
35
35
  # Configure Rails-specific functionality
36
36
  # Core functionality is provided by the ragdoll gem
37
+
38
+ # Ensure ViewComponent autoloading for engine components
39
+ app.config.autoload_paths += ["#{root}/app/components"]
40
+
41
+ # Ensure Services autoloading for engine services
42
+ app.config.autoload_paths += ["#{root}/app/services"]
43
+
44
+ # Configure ViewComponent
45
+ if ::Rails.env.development? && app.config.respond_to?(:view_component)
46
+ app.config.view_component.preview_paths ||= []
47
+ app.config.view_component.preview_paths << "#{root}/spec/components/previews"
48
+ end
49
+
50
+ # Configure ActionCable for the engine
51
+ # Provide default ActionCable configuration if not already set
52
+ app.config.action_cable.mount_path ||= '/cable'
53
+
54
+ # Set default adapter if not already configured
55
+ unless app.config.action_cable.adapter
56
+ if ::Rails.env.development?
57
+ app.config.action_cable.adapter = 'redis'
58
+ app.config.action_cable.url = 'redis://localhost:6379/0'
59
+ elsif ::Rails.env.test?
60
+ app.config.action_cable.adapter = 'test'
61
+ elsif ::Rails.env.production?
62
+ app.config.action_cable.adapter = 'redis'
63
+ app.config.action_cable.url = ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" }
64
+ app.config.action_cable.channel_prefix = 'ragdoll_rails_production'
65
+ end
66
+ end
37
67
  end
38
68
 
69
+
39
70
  # Ensure models are eager loaded in production
40
71
  initializer "ragdoll.eager_load", after: "finisher_hook" do |app|
41
72
  if ::Rails.env.production?
@@ -4,6 +4,6 @@
4
4
 
5
5
  module Ragdoll
6
6
  module Rails
7
- VERSION = "0.1.9"
7
+ VERSION = "0.1.11"
8
8
  end
9
9
  end