ragdoll-rails 0.1.8 → 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.
- checksums.yaml +4 -4
- data/README.md +18 -21
- data/app/assets/javascripts/ragdoll/application.js +129 -0
- data/app/assets/javascripts/ragdoll/bulk_upload_status.js +454 -0
- data/app/assets/stylesheets/ragdoll/application.css +84 -0
- data/app/assets/stylesheets/ragdoll/bulk_upload_status.css +379 -0
- data/app/channels/application_cable/channel.rb +6 -0
- data/app/channels/application_cable/connection.rb +6 -0
- data/app/channels/ragdoll/bulk_upload_status_channel.rb +27 -0
- data/app/channels/ragdoll/file_processing_channel.rb +26 -0
- data/app/components/ragdoll/alert_component.html.erb +4 -0
- data/app/components/ragdoll/alert_component.rb +32 -0
- data/app/components/ragdoll/application_component.rb +6 -0
- data/app/components/ragdoll/card_component.html.erb +15 -0
- data/app/components/ragdoll/card_component.rb +21 -0
- data/app/components/ragdoll/document_list_component.html.erb +41 -0
- data/app/components/ragdoll/document_list_component.rb +13 -0
- data/app/components/ragdoll/document_table_component.html.erb +76 -0
- data/app/components/ragdoll/document_table_component.rb +13 -0
- data/app/components/ragdoll/empty_state_component.html.erb +12 -0
- data/app/components/ragdoll/empty_state_component.rb +17 -0
- data/app/components/ragdoll/flash_messages_component.html.erb +3 -0
- data/app/components/ragdoll/flash_messages_component.rb +37 -0
- data/app/components/ragdoll/navbar_component.html.erb +24 -0
- data/app/components/ragdoll/navbar_component.rb +31 -0
- data/app/components/ragdoll/page_header_component.html.erb +13 -0
- data/app/components/ragdoll/page_header_component.rb +15 -0
- data/app/components/ragdoll/stats_card_component.html.erb +11 -0
- data/app/components/ragdoll/stats_card_component.rb +17 -0
- data/app/components/ragdoll/status_badge_component.html.erb +3 -0
- data/app/components/ragdoll/status_badge_component.rb +30 -0
- data/app/controllers/ragdoll/api/v1/analytics_controller.rb +72 -0
- data/app/controllers/ragdoll/api/v1/base_controller.rb +29 -0
- data/app/controllers/ragdoll/api/v1/documents_controller.rb +148 -0
- data/app/controllers/ragdoll/api/v1/search_controller.rb +87 -0
- data/app/controllers/ragdoll/api/v1/system_controller.rb +97 -0
- data/app/controllers/ragdoll/application_controller.rb +17 -0
- data/app/controllers/ragdoll/configuration_controller.rb +82 -0
- data/app/controllers/ragdoll/dashboard_controller.rb +98 -0
- data/app/controllers/ragdoll/documents_controller.rb +460 -0
- data/app/controllers/ragdoll/documents_controller_backup.rb +68 -0
- data/app/controllers/ragdoll/jobs_controller.rb +116 -0
- data/app/controllers/ragdoll/search_controller.rb +368 -0
- data/app/jobs/application_job.rb +9 -0
- data/app/jobs/ragdoll/bulk_document_processing_job.rb +280 -0
- data/app/jobs/ragdoll/process_file_job.rb +166 -0
- data/app/services/ragdoll/worker_health_service.rb +111 -0
- data/app/views/layouts/ragdoll/application.html.erb +162 -0
- data/app/views/ragdoll/dashboard/analytics.html.erb +333 -0
- data/app/views/ragdoll/dashboard/index.html.erb +208 -0
- data/app/views/ragdoll/documents/edit.html.erb +91 -0
- data/app/views/ragdoll/documents/index.html.erb +302 -0
- data/app/views/ragdoll/documents/new.html.erb +1518 -0
- data/app/views/ragdoll/documents/show.html.erb +188 -0
- data/app/views/ragdoll/documents/upload_results.html.erb +248 -0
- data/app/views/ragdoll/jobs/index.html.erb +669 -0
- data/app/views/ragdoll/jobs/show.html.erb +129 -0
- data/app/views/ragdoll/search/index.html.erb +324 -0
- data/config/cable.yml +12 -0
- data/config/routes.rb +57 -2
- data/lib/generators/ragdoll/init/templates/INSTALL +3 -2
- data/lib/generators/ragdoll/init_generator.rb +68 -0
- data/lib/ragdoll/rails/engine.rb +48 -0
- data/lib/ragdoll/rails/version.rb +1 -1
- metadata +231 -6
- data/lib/generators/ragdoll/init/init_generator.rb +0 -26
@@ -0,0 +1,302 @@
|
|
1
|
+
<% content_for :title, "Documents - Ragdoll Engine" %>
|
2
|
+
|
3
|
+
<div class="row">
|
4
|
+
<div class="col-12">
|
5
|
+
<div class="d-flex justify-content-between align-items-center mb-4">
|
6
|
+
<h1><i class="fas fa-file-alt"></i> Documents</h1>
|
7
|
+
<%= link_to "Add Document", ragdoll.new_document_path, class: "btn btn-primary" %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="row mb-4">
|
13
|
+
<div class="col-12">
|
14
|
+
<div class="card">
|
15
|
+
<div class="card-header">
|
16
|
+
<h5><i class="fas fa-filter"></i> Filters</h5>
|
17
|
+
</div>
|
18
|
+
<div class="card-body">
|
19
|
+
<%= form_with url: ragdoll.documents_path, method: :get, local: true, class: "row g-3" do |form| %>
|
20
|
+
<div class="col-md-3">
|
21
|
+
<%= form.text_field :search, placeholder: "Search documents...", value: params[:search], class: "form-control" %>
|
22
|
+
</div>
|
23
|
+
<div class="col-md-3">
|
24
|
+
<%= form.select :document_type, options_for_select([["All Types", ""]] + @document_types.map { |type| [type.titleize, type] }, params[:document_type]), {}, { class: "form-select" } %>
|
25
|
+
</div>
|
26
|
+
<div class="col-md-3">
|
27
|
+
<%= form.select :status, options_for_select([["All Statuses", ""]] + @statuses.map { |status| [status.titleize, status] }, params[:status]), {}, { class: "form-select" } %>
|
28
|
+
</div>
|
29
|
+
<div class="col-md-3">
|
30
|
+
<%= form.submit "Filter", class: "btn btn-outline-primary" %>
|
31
|
+
<%= link_to "Clear", ragdoll.documents_path, class: "btn btn-outline-secondary" %>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
<% if @documents.any? %>
|
40
|
+
<div class="row">
|
41
|
+
<div class="col-12">
|
42
|
+
<div class="card">
|
43
|
+
<div class="card-header">
|
44
|
+
<div class="d-flex justify-content-between align-items-center">
|
45
|
+
<h5 class="mb-0"><i class="fas fa-list"></i> Document List</h5>
|
46
|
+
<div class="d-flex align-items-center gap-2">
|
47
|
+
<!-- View Toggle Buttons -->
|
48
|
+
<div class="btn-group btn-group-sm me-3" role="group" aria-label="View toggle">
|
49
|
+
<button type="button" class="btn btn-outline-secondary" id="card-view-btn" onclick="switchToCardView()">
|
50
|
+
<i class="fas fa-th-large"></i> Cards
|
51
|
+
</button>
|
52
|
+
<button type="button" class="btn btn-outline-secondary" id="table-view-btn" onclick="switchToTableView()">
|
53
|
+
<i class="fas fa-table"></i> Table
|
54
|
+
</button>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<!-- Bulk Action Buttons -->
|
58
|
+
<button class="btn btn-outline-danger btn-sm" onclick="bulkDelete()">
|
59
|
+
<i class="fas fa-trash"></i> Delete Selected
|
60
|
+
</button>
|
61
|
+
<button class="btn btn-outline-warning btn-sm" onclick="bulkReprocess()">
|
62
|
+
<i class="fas fa-sync"></i> Reprocess Selected
|
63
|
+
</button>
|
64
|
+
</div>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
<div class="card-body">
|
68
|
+
<!-- Card View -->
|
69
|
+
<div id="card-view" style="display: block;">
|
70
|
+
<%= render Ragdoll::DocumentListComponent.new(documents: @documents) %>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<!-- Table View -->
|
74
|
+
<div id="table-view" style="display: none;">
|
75
|
+
<%= render Ragdoll::DocumentTableComponent.new(documents: @documents) %>
|
76
|
+
</div>
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
</div>
|
81
|
+
<% else %>
|
82
|
+
<div class="row">
|
83
|
+
<div class="col-12">
|
84
|
+
<%= render Ragdoll::EmptyStateComponent.new(
|
85
|
+
title: "No documents found",
|
86
|
+
message: "Get started by adding your first document to the system.",
|
87
|
+
icon: "fas fa-file-alt",
|
88
|
+
action_path: ragdoll.new_document_path,
|
89
|
+
action_text: "Add Document"
|
90
|
+
) %>
|
91
|
+
</div>
|
92
|
+
</div>
|
93
|
+
<% end %>
|
94
|
+
|
95
|
+
<%= content_for :javascript do %>
|
96
|
+
<script>
|
97
|
+
// View switching functions
|
98
|
+
function switchToCardView() {
|
99
|
+
document.getElementById('card-view').style.display = 'block';
|
100
|
+
document.getElementById('table-view').style.display = 'none';
|
101
|
+
document.getElementById('card-view-btn').classList.add('active');
|
102
|
+
document.getElementById('table-view-btn').classList.remove('active');
|
103
|
+
localStorage.setItem('ragdoll-document-view', 'card');
|
104
|
+
}
|
105
|
+
|
106
|
+
function switchToTableView() {
|
107
|
+
document.getElementById('card-view').style.display = 'none';
|
108
|
+
document.getElementById('table-view').style.display = 'block';
|
109
|
+
document.getElementById('table-view-btn').classList.add('active');
|
110
|
+
document.getElementById('card-view-btn').classList.remove('active');
|
111
|
+
localStorage.setItem('ragdoll-document-view', 'table');
|
112
|
+
}
|
113
|
+
|
114
|
+
// Attach event listeners to document checkboxes
|
115
|
+
function attachCheckboxListeners() {
|
116
|
+
document.querySelectorAll('.document-checkbox').forEach(checkbox => {
|
117
|
+
checkbox.removeEventListener('change', updateSelectAllState); // Remove first to avoid duplicates
|
118
|
+
checkbox.addEventListener('change', updateSelectAllState);
|
119
|
+
});
|
120
|
+
updateSelectAllState(); // Initialize state
|
121
|
+
}
|
122
|
+
|
123
|
+
// Initialize view based on saved preference
|
124
|
+
document.addEventListener('DOMContentLoaded', function() {
|
125
|
+
const savedView = localStorage.getItem('ragdoll-document-view') || 'card';
|
126
|
+
if (savedView === 'table') {
|
127
|
+
switchToTableView();
|
128
|
+
} else {
|
129
|
+
switchToCardView();
|
130
|
+
}
|
131
|
+
|
132
|
+
// Attach listeners after view is set
|
133
|
+
setTimeout(attachCheckboxListeners, 100);
|
134
|
+
});
|
135
|
+
|
136
|
+
function toggleAll(source) {
|
137
|
+
// Get all document checkboxes
|
138
|
+
const checkboxes = document.querySelectorAll('.document-checkbox');
|
139
|
+
const selectAllCard = document.getElementById('select-all');
|
140
|
+
const selectAllTable = document.getElementById('select-all-table');
|
141
|
+
|
142
|
+
// Set all checkboxes to match the source checkbox state
|
143
|
+
checkboxes.forEach(checkbox => {
|
144
|
+
checkbox.checked = source.checked;
|
145
|
+
});
|
146
|
+
|
147
|
+
// Sync both select-all checkboxes
|
148
|
+
if (selectAllCard) {
|
149
|
+
selectAllCard.checked = source.checked;
|
150
|
+
selectAllCard.indeterminate = false;
|
151
|
+
}
|
152
|
+
if (selectAllTable) {
|
153
|
+
selectAllTable.checked = source.checked;
|
154
|
+
selectAllTable.indeterminate = false;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
function updateSelectAllState() {
|
159
|
+
// Get all document checkboxes
|
160
|
+
const checkboxes = document.querySelectorAll('.document-checkbox');
|
161
|
+
const selectAllCard = document.getElementById('select-all');
|
162
|
+
const selectAllTable = document.getElementById('select-all-table');
|
163
|
+
|
164
|
+
if (checkboxes.length === 0) return;
|
165
|
+
|
166
|
+
// Count checked boxes
|
167
|
+
const checkedCount = Array.from(checkboxes).filter(cb => cb.checked).length;
|
168
|
+
|
169
|
+
// Determine state
|
170
|
+
let isChecked = false;
|
171
|
+
let isIndeterminate = false;
|
172
|
+
|
173
|
+
if (checkedCount === 0) {
|
174
|
+
// None checked
|
175
|
+
isChecked = false;
|
176
|
+
isIndeterminate = false;
|
177
|
+
} else if (checkedCount === checkboxes.length) {
|
178
|
+
// All checked
|
179
|
+
isChecked = true;
|
180
|
+
isIndeterminate = false;
|
181
|
+
} else {
|
182
|
+
// Some checked
|
183
|
+
isChecked = false;
|
184
|
+
isIndeterminate = true;
|
185
|
+
}
|
186
|
+
|
187
|
+
// Update both select-all checkboxes
|
188
|
+
if (selectAllCard) {
|
189
|
+
selectAllCard.checked = isChecked;
|
190
|
+
selectAllCard.indeterminate = isIndeterminate;
|
191
|
+
}
|
192
|
+
if (selectAllTable) {
|
193
|
+
selectAllTable.checked = isChecked;
|
194
|
+
selectAllTable.indeterminate = isIndeterminate;
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
|
199
|
+
function reprocessDocument(documentId) {
|
200
|
+
if (confirm('Are you sure you want to reprocess this document?')) {
|
201
|
+
// Create a form with proper CSRF token for individual reprocess action
|
202
|
+
const form = document.createElement('form');
|
203
|
+
form.method = 'POST';
|
204
|
+
form.action = `<%= ragdoll.documents_path %>/${documentId}/reprocess`;
|
205
|
+
|
206
|
+
// Add CSRF token
|
207
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
208
|
+
const csrfInput = document.createElement('input');
|
209
|
+
csrfInput.type = 'hidden';
|
210
|
+
csrfInput.name = 'authenticity_token';
|
211
|
+
csrfInput.value = csrfToken;
|
212
|
+
form.appendChild(csrfInput);
|
213
|
+
|
214
|
+
document.body.appendChild(form);
|
215
|
+
form.submit();
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
function bulkDelete() {
|
220
|
+
const checkedBoxes = document.querySelectorAll('.document-checkbox:checked');
|
221
|
+
if (checkedBoxes.length === 0) {
|
222
|
+
alert('Please select at least one document to delete.');
|
223
|
+
return;
|
224
|
+
}
|
225
|
+
|
226
|
+
if (confirm(`Are you sure you want to delete ${checkedBoxes.length} selected documents?`)) {
|
227
|
+
// Create a new form with proper CSRF token for delete action
|
228
|
+
const form = document.createElement('form');
|
229
|
+
form.method = 'POST';
|
230
|
+
form.action = '<%= ragdoll.bulk_delete_documents_path %>';
|
231
|
+
|
232
|
+
// Add CSRF token
|
233
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
234
|
+
const csrfInput = document.createElement('input');
|
235
|
+
csrfInput.type = 'hidden';
|
236
|
+
csrfInput.name = 'authenticity_token';
|
237
|
+
csrfInput.value = csrfToken;
|
238
|
+
form.appendChild(csrfInput);
|
239
|
+
|
240
|
+
// Add selected document IDs
|
241
|
+
checkedBoxes.forEach(checkbox => {
|
242
|
+
const input = document.createElement('input');
|
243
|
+
input.type = 'hidden';
|
244
|
+
input.name = 'document_ids[]';
|
245
|
+
input.value = checkbox.value;
|
246
|
+
form.appendChild(input);
|
247
|
+
});
|
248
|
+
|
249
|
+
document.body.appendChild(form);
|
250
|
+
form.submit();
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
function bulkReprocess() {
|
255
|
+
const checkedBoxes = document.querySelectorAll('.document-checkbox:checked');
|
256
|
+
if (checkedBoxes.length === 0) {
|
257
|
+
alert('Please select at least one document to reprocess.');
|
258
|
+
return;
|
259
|
+
}
|
260
|
+
|
261
|
+
if (confirm(`Are you sure you want to reprocess ${checkedBoxes.length} selected documents?`)) {
|
262
|
+
// Create a new form with proper CSRF token for reprocess action
|
263
|
+
const form = document.createElement('form');
|
264
|
+
form.method = 'POST';
|
265
|
+
form.action = '<%= ragdoll.bulk_reprocess_documents_path %>';
|
266
|
+
|
267
|
+
// Add CSRF token
|
268
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
269
|
+
const csrfInput = document.createElement('input');
|
270
|
+
csrfInput.type = 'hidden';
|
271
|
+
csrfInput.name = 'authenticity_token';
|
272
|
+
csrfInput.value = csrfToken;
|
273
|
+
form.appendChild(csrfInput);
|
274
|
+
|
275
|
+
// Add selected document IDs
|
276
|
+
checkedBoxes.forEach(checkbox => {
|
277
|
+
const input = document.createElement('input');
|
278
|
+
input.type = 'hidden';
|
279
|
+
input.name = 'document_ids[]';
|
280
|
+
input.value = checkbox.value;
|
281
|
+
form.appendChild(input);
|
282
|
+
});
|
283
|
+
|
284
|
+
document.body.appendChild(form);
|
285
|
+
form.submit();
|
286
|
+
}
|
287
|
+
}
|
288
|
+
|
289
|
+
// Initialize Bootstrap tooltips
|
290
|
+
document.addEventListener('DOMContentLoaded', function () {
|
291
|
+
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
292
|
+
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
293
|
+
return new bootstrap.Tooltip(tooltipTriggerEl, {
|
294
|
+
delay: { "show": 500, "hide": 100 },
|
295
|
+
placement: 'top',
|
296
|
+
boundary: 'viewport',
|
297
|
+
fallbackPlacements: ['top', 'bottom']
|
298
|
+
});
|
299
|
+
});
|
300
|
+
});
|
301
|
+
</script>
|
302
|
+
<% end %>
|