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.
- checksums.yaml +4 -4
- 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 +56 -1
- data/lib/ragdoll/rails/engine.rb +32 -1
- data/lib/ragdoll/rails/version.rb +1 -1
- metadata +86 -1
@@ -0,0 +1,379 @@
|
|
1
|
+
/* Bulk Upload Status Popup Styles */
|
2
|
+
.bulk-upload-status-container {
|
3
|
+
position: fixed;
|
4
|
+
top: 20px;
|
5
|
+
right: 20px;
|
6
|
+
width: 400px;
|
7
|
+
max-width: 90vw;
|
8
|
+
max-height: 80vh;
|
9
|
+
background: #ffffff;
|
10
|
+
border: 1px solid #e1e5e9;
|
11
|
+
border-radius: 12px;
|
12
|
+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
|
13
|
+
z-index: 10000;
|
14
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
15
|
+
font-size: 14px;
|
16
|
+
opacity: 0;
|
17
|
+
transform: translateY(-20px) scale(0.95);
|
18
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
19
|
+
pointer-events: none;
|
20
|
+
overflow: hidden;
|
21
|
+
}
|
22
|
+
|
23
|
+
.bulk-upload-status-container.visible {
|
24
|
+
opacity: 1;
|
25
|
+
transform: translateY(0) scale(1);
|
26
|
+
pointer-events: auto;
|
27
|
+
}
|
28
|
+
|
29
|
+
.bulk-upload-status-container.minimized .bulk-upload-status-content {
|
30
|
+
display: none;
|
31
|
+
}
|
32
|
+
|
33
|
+
.bulk-upload-status-container.minimized {
|
34
|
+
height: auto;
|
35
|
+
}
|
36
|
+
|
37
|
+
/* Header */
|
38
|
+
.bulk-upload-status-header {
|
39
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
40
|
+
color: white;
|
41
|
+
padding: 16px 20px;
|
42
|
+
display: flex;
|
43
|
+
justify-content: space-between;
|
44
|
+
align-items: center;
|
45
|
+
cursor: grab;
|
46
|
+
user-select: none;
|
47
|
+
}
|
48
|
+
|
49
|
+
.bulk-upload-status-header:active {
|
50
|
+
cursor: grabbing;
|
51
|
+
}
|
52
|
+
|
53
|
+
.bulk-upload-status-title {
|
54
|
+
display: flex;
|
55
|
+
align-items: center;
|
56
|
+
gap: 8px;
|
57
|
+
font-weight: 600;
|
58
|
+
font-size: 16px;
|
59
|
+
}
|
60
|
+
|
61
|
+
.bulk-upload-status-title i {
|
62
|
+
font-size: 18px;
|
63
|
+
}
|
64
|
+
|
65
|
+
.bulk-upload-status-controls {
|
66
|
+
display: flex;
|
67
|
+
gap: 8px;
|
68
|
+
}
|
69
|
+
|
70
|
+
.bulk-upload-status-controls button {
|
71
|
+
background: rgba(255, 255, 255, 0.2);
|
72
|
+
border: none;
|
73
|
+
color: white;
|
74
|
+
width: 32px;
|
75
|
+
height: 32px;
|
76
|
+
border-radius: 6px;
|
77
|
+
display: flex;
|
78
|
+
align-items: center;
|
79
|
+
justify-content: center;
|
80
|
+
cursor: pointer;
|
81
|
+
transition: background-color 0.2s ease;
|
82
|
+
}
|
83
|
+
|
84
|
+
.bulk-upload-status-controls button:hover {
|
85
|
+
background: rgba(255, 255, 255, 0.3);
|
86
|
+
}
|
87
|
+
|
88
|
+
.bulk-upload-status-controls button:active {
|
89
|
+
background: rgba(255, 255, 255, 0.4);
|
90
|
+
}
|
91
|
+
|
92
|
+
/* Content */
|
93
|
+
.bulk-upload-status-content {
|
94
|
+
max-height: 500px;
|
95
|
+
overflow-y: auto;
|
96
|
+
padding: 0;
|
97
|
+
}
|
98
|
+
|
99
|
+
.no-uploads-message {
|
100
|
+
padding: 24px 20px;
|
101
|
+
text-align: center;
|
102
|
+
color: #6b7280;
|
103
|
+
font-style: italic;
|
104
|
+
}
|
105
|
+
|
106
|
+
/* Upload Items */
|
107
|
+
.upload-item {
|
108
|
+
border-bottom: 1px solid #f3f4f6;
|
109
|
+
padding: 20px;
|
110
|
+
background: #ffffff;
|
111
|
+
transition: background-color 0.2s ease;
|
112
|
+
}
|
113
|
+
|
114
|
+
.upload-item:last-child {
|
115
|
+
border-bottom: none;
|
116
|
+
}
|
117
|
+
|
118
|
+
.upload-item:hover {
|
119
|
+
background: #f9fafb;
|
120
|
+
}
|
121
|
+
|
122
|
+
.upload-item.status-starting {
|
123
|
+
background: #fef3c7;
|
124
|
+
}
|
125
|
+
|
126
|
+
.upload-item.status-processing {
|
127
|
+
background: #dbeafe;
|
128
|
+
}
|
129
|
+
|
130
|
+
.upload-item.status-completed {
|
131
|
+
background: #d1fae5;
|
132
|
+
}
|
133
|
+
|
134
|
+
.upload-item.status-failed {
|
135
|
+
background: #fee2e2;
|
136
|
+
}
|
137
|
+
|
138
|
+
/* Upload Header */
|
139
|
+
.upload-header {
|
140
|
+
display: flex;
|
141
|
+
justify-content: space-between;
|
142
|
+
align-items: center;
|
143
|
+
margin-bottom: 12px;
|
144
|
+
}
|
145
|
+
|
146
|
+
.upload-title {
|
147
|
+
display: flex;
|
148
|
+
align-items: center;
|
149
|
+
gap: 8px;
|
150
|
+
font-weight: 600;
|
151
|
+
color: #374151;
|
152
|
+
}
|
153
|
+
|
154
|
+
.upload-percentage {
|
155
|
+
font-weight: 700;
|
156
|
+
color: #667eea;
|
157
|
+
font-size: 16px;
|
158
|
+
}
|
159
|
+
|
160
|
+
/* Progress Bar */
|
161
|
+
.progress-bar {
|
162
|
+
width: 100%;
|
163
|
+
height: 8px;
|
164
|
+
background: #e5e7eb;
|
165
|
+
border-radius: 4px;
|
166
|
+
overflow: hidden;
|
167
|
+
margin-bottom: 16px;
|
168
|
+
}
|
169
|
+
|
170
|
+
.progress-fill {
|
171
|
+
height: 100%;
|
172
|
+
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
173
|
+
transition: width 0.3s ease;
|
174
|
+
border-radius: 4px;
|
175
|
+
}
|
176
|
+
|
177
|
+
.status-failed .progress-fill {
|
178
|
+
background: linear-gradient(90deg, #ef4444 0%, #dc2626 100%);
|
179
|
+
}
|
180
|
+
|
181
|
+
.status-completed .progress-fill {
|
182
|
+
background: linear-gradient(90deg, #10b981 0%, #059669 100%);
|
183
|
+
}
|
184
|
+
|
185
|
+
/* Upload Details */
|
186
|
+
.upload-details {
|
187
|
+
space-y: 12px;
|
188
|
+
}
|
189
|
+
|
190
|
+
.upload-stats {
|
191
|
+
display: flex;
|
192
|
+
gap: 16px;
|
193
|
+
flex-wrap: wrap;
|
194
|
+
margin-bottom: 12px;
|
195
|
+
}
|
196
|
+
|
197
|
+
.upload-stats > span {
|
198
|
+
display: flex;
|
199
|
+
align-items: center;
|
200
|
+
gap: 4px;
|
201
|
+
font-size: 13px;
|
202
|
+
color: #6b7280;
|
203
|
+
}
|
204
|
+
|
205
|
+
.processed-count {
|
206
|
+
color: #059669 !important;
|
207
|
+
font-weight: 600;
|
208
|
+
}
|
209
|
+
|
210
|
+
.failed-count {
|
211
|
+
color: #dc2626 !important;
|
212
|
+
font-weight: 600;
|
213
|
+
}
|
214
|
+
|
215
|
+
.eta {
|
216
|
+
color: #667eea !important;
|
217
|
+
font-weight: 500;
|
218
|
+
}
|
219
|
+
|
220
|
+
/* Current File */
|
221
|
+
.current-file {
|
222
|
+
display: flex;
|
223
|
+
align-items: center;
|
224
|
+
gap: 8px;
|
225
|
+
padding: 8px 12px;
|
226
|
+
background: rgba(102, 126, 234, 0.1);
|
227
|
+
border-radius: 6px;
|
228
|
+
margin-bottom: 12px;
|
229
|
+
font-size: 13px;
|
230
|
+
color: #4338ca;
|
231
|
+
}
|
232
|
+
|
233
|
+
.current-file i {
|
234
|
+
color: #667eea;
|
235
|
+
}
|
236
|
+
|
237
|
+
/* Messages */
|
238
|
+
.completion-message,
|
239
|
+
.error-message {
|
240
|
+
display: flex;
|
241
|
+
align-items: center;
|
242
|
+
gap: 8px;
|
243
|
+
padding: 12px;
|
244
|
+
border-radius: 6px;
|
245
|
+
font-size: 13px;
|
246
|
+
font-weight: 500;
|
247
|
+
margin-bottom: 12px;
|
248
|
+
}
|
249
|
+
|
250
|
+
.completion-message {
|
251
|
+
background: #d1fae5;
|
252
|
+
color: #047857;
|
253
|
+
border: 1px solid #a7f3d0;
|
254
|
+
}
|
255
|
+
|
256
|
+
.error-message {
|
257
|
+
background: #fee2e2;
|
258
|
+
color: #dc2626;
|
259
|
+
border: 1px solid #fca5a5;
|
260
|
+
}
|
261
|
+
|
262
|
+
/* Error List */
|
263
|
+
.error-list {
|
264
|
+
margin-top: 12px;
|
265
|
+
}
|
266
|
+
|
267
|
+
.error-list details {
|
268
|
+
background: #fef2f2;
|
269
|
+
border: 1px solid #fecaca;
|
270
|
+
border-radius: 6px;
|
271
|
+
padding: 8px 12px;
|
272
|
+
}
|
273
|
+
|
274
|
+
.error-list summary {
|
275
|
+
font-weight: 600;
|
276
|
+
color: #dc2626;
|
277
|
+
cursor: pointer;
|
278
|
+
font-size: 13px;
|
279
|
+
}
|
280
|
+
|
281
|
+
.error-list summary:hover {
|
282
|
+
color: #b91c1c;
|
283
|
+
}
|
284
|
+
|
285
|
+
.error-list ul {
|
286
|
+
margin: 8px 0 0 0;
|
287
|
+
padding-left: 16px;
|
288
|
+
list-style-type: disc;
|
289
|
+
}
|
290
|
+
|
291
|
+
.error-list li {
|
292
|
+
color: #7f1d1d;
|
293
|
+
font-size: 12px;
|
294
|
+
margin: 4px 0;
|
295
|
+
word-break: break-word;
|
296
|
+
}
|
297
|
+
|
298
|
+
/* Animations */
|
299
|
+
@keyframes pulse {
|
300
|
+
0%, 100% {
|
301
|
+
opacity: 1;
|
302
|
+
}
|
303
|
+
50% {
|
304
|
+
opacity: 0.7;
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
308
|
+
.status-processing .upload-title i {
|
309
|
+
animation: pulse 2s infinite;
|
310
|
+
}
|
311
|
+
|
312
|
+
/* Responsive Design */
|
313
|
+
@media (max-width: 768px) {
|
314
|
+
.bulk-upload-status-container {
|
315
|
+
width: 320px;
|
316
|
+
right: 10px;
|
317
|
+
top: 10px;
|
318
|
+
}
|
319
|
+
|
320
|
+
.upload-stats {
|
321
|
+
flex-direction: column;
|
322
|
+
gap: 8px;
|
323
|
+
}
|
324
|
+
|
325
|
+
.bulk-upload-status-header {
|
326
|
+
padding: 12px 16px;
|
327
|
+
}
|
328
|
+
|
329
|
+
.upload-item {
|
330
|
+
padding: 16px;
|
331
|
+
}
|
332
|
+
}
|
333
|
+
|
334
|
+
@media (max-width: 480px) {
|
335
|
+
.bulk-upload-status-container {
|
336
|
+
width: calc(100vw - 20px);
|
337
|
+
right: 10px;
|
338
|
+
left: 10px;
|
339
|
+
}
|
340
|
+
}
|
341
|
+
|
342
|
+
/* Dark mode support */
|
343
|
+
@media (prefers-color-scheme: dark) {
|
344
|
+
.bulk-upload-status-container {
|
345
|
+
background: #1f2937;
|
346
|
+
border-color: #374151;
|
347
|
+
color: #f9fafb;
|
348
|
+
}
|
349
|
+
|
350
|
+
.upload-item {
|
351
|
+
background: #1f2937;
|
352
|
+
border-color: #374151;
|
353
|
+
}
|
354
|
+
|
355
|
+
.upload-item:hover {
|
356
|
+
background: #374151;
|
357
|
+
}
|
358
|
+
|
359
|
+
.upload-title {
|
360
|
+
color: #f9fafb;
|
361
|
+
}
|
362
|
+
|
363
|
+
.no-uploads-message {
|
364
|
+
color: #9ca3af;
|
365
|
+
}
|
366
|
+
|
367
|
+
.progress-bar {
|
368
|
+
background: #374151;
|
369
|
+
}
|
370
|
+
|
371
|
+
.upload-stats > span {
|
372
|
+
color: #9ca3af;
|
373
|
+
}
|
374
|
+
|
375
|
+
.current-file {
|
376
|
+
background: rgba(102, 126, 234, 0.2);
|
377
|
+
color: #93c5fd;
|
378
|
+
}
|
379
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Ragdoll
|
2
|
+
class BulkUploadStatusChannel < ApplicationCable::Channel
|
3
|
+
def subscribed
|
4
|
+
session_id = params[:session_id]
|
5
|
+
|
6
|
+
if session_id.present?
|
7
|
+
stream_from "bulk_upload_status_#{session_id}"
|
8
|
+
logger.info "📡 Client subscribed to bulk upload status for session: #{session_id}"
|
9
|
+
else
|
10
|
+
reject
|
11
|
+
logger.warn "⚠️ Bulk upload status subscription rejected: missing session_id"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def unsubscribed
|
16
|
+
logger.info "📡 Client unsubscribed from bulk upload status"
|
17
|
+
end
|
18
|
+
|
19
|
+
def ping(data)
|
20
|
+
# Respond to client ping to maintain connection
|
21
|
+
transmit({
|
22
|
+
type: 'pong',
|
23
|
+
timestamp: Time.current.iso8601
|
24
|
+
})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ragdoll
|
4
|
+
class FileProcessingChannel < ApplicationCable::Channel
|
5
|
+
def subscribed
|
6
|
+
stream_from "ragdoll_file_processing_#{params[:session_id]}"
|
7
|
+
puts "📡 Ragdoll::FileProcessingChannel subscribed to ragdoll_file_processing_#{params[:session_id]}"
|
8
|
+
logger.info "📡 Ragdoll::FileProcessingChannel subscribed to ragdoll_file_processing_#{params[:session_id]}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def unsubscribed
|
12
|
+
puts "📡 Ragdoll::FileProcessingChannel unsubscribed from ragdoll_file_processing_#{params[:session_id]}"
|
13
|
+
logger.info "📡 Ragdoll::FileProcessingChannel unsubscribed from ragdoll_file_processing_#{params[:session_id]}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_connection
|
17
|
+
puts "🏓 Received test_connection ping from session: #{params[:session_id]}"
|
18
|
+
logger.info "🏓 Received test_connection ping from session: #{params[:session_id]}"
|
19
|
+
ActionCable.server.broadcast("ragdoll_file_processing_#{params[:session_id]}", {
|
20
|
+
type: 'ping',
|
21
|
+
message: 'Connection test successful',
|
22
|
+
timestamp: Time.current.to_f
|
23
|
+
})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ragdoll
|
4
|
+
class AlertComponent < ApplicationComponent
|
5
|
+
def initialize(message:, type: 'info', dismissible: true, **options)
|
6
|
+
@message = message
|
7
|
+
@type = type
|
8
|
+
@dismissible = dismissible
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :message, :type, :dismissible, :options
|
15
|
+
|
16
|
+
def alert_classes
|
17
|
+
classes = ['alert', "alert-#{type}"]
|
18
|
+
classes << 'alert-dismissible' if dismissible
|
19
|
+
classes << 'fade show' if dismissible
|
20
|
+
classes << options[:class] if options[:class]
|
21
|
+
classes.join(' ')
|
22
|
+
end
|
23
|
+
|
24
|
+
def dismiss_button
|
25
|
+
return unless dismissible
|
26
|
+
|
27
|
+
content_tag :button, type: 'button', class: 'btn-close', data: { bs_dismiss: 'alert' } do
|
28
|
+
''
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<div class="<%= card_classes %>">
|
2
|
+
<% if title %>
|
3
|
+
<div class="card-header">
|
4
|
+
<h5>
|
5
|
+
<% if icon %>
|
6
|
+
<i class="<%= icon %>"></i>
|
7
|
+
<% end %>
|
8
|
+
<%= title %>
|
9
|
+
</h5>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
<div class="card-body">
|
13
|
+
<%= content %>
|
14
|
+
</div>
|
15
|
+
</div>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ragdoll
|
4
|
+
class CardComponent < ApplicationComponent
|
5
|
+
def initialize(title: nil, icon: nil, **options)
|
6
|
+
@title = title
|
7
|
+
@icon = icon
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_reader :title, :icon, :options
|
14
|
+
|
15
|
+
def card_classes
|
16
|
+
classes = ['card']
|
17
|
+
classes << options[:class] if options[:class]
|
18
|
+
classes.join(' ')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<div class="mb-3 d-flex align-items-center">
|
2
|
+
<input type="checkbox" id="select-all" class="form-check-input me-2" onclick="toggleAll(this)">
|
3
|
+
<label class="form-check-label text-muted">Select All</label>
|
4
|
+
</div>
|
5
|
+
<div class="row">
|
6
|
+
<% documents.each do |document| %>
|
7
|
+
<div class="col-md-6 col-lg-4 mb-4">
|
8
|
+
<div class="card h-100">
|
9
|
+
<div class="card-header d-flex justify-content-between align-items-center">
|
10
|
+
<div class="d-flex align-items-center">
|
11
|
+
<input type="checkbox" class="document-checkbox form-check-input me-2" value="<%= document.id %>">
|
12
|
+
<h6 class="card-title mb-0"><%= document.title %></h6>
|
13
|
+
</div>
|
14
|
+
<span class="badge bg-<%= document.status == 'processed' ? 'success' : document.status == 'failed' ? 'danger' : 'warning' %>">
|
15
|
+
<%= document.status.humanize %>
|
16
|
+
</span>
|
17
|
+
</div>
|
18
|
+
<div class="card-body">
|
19
|
+
<p class="card-text text-muted small mb-2">
|
20
|
+
<i class="fas fa-file"></i> <%= document.document_type %>
|
21
|
+
</p>
|
22
|
+
<p class="card-text text-muted small mb-2">
|
23
|
+
<i class="fas fa-weight"></i> <%= number_to_human_size(document.metadata['file_size']) if document.metadata && document.metadata['file_size'] %>
|
24
|
+
</p>
|
25
|
+
<p class="card-text text-muted small">
|
26
|
+
<i class="fas fa-clock"></i> <%= time_ago_in_words(document.created_at) %> ago
|
27
|
+
</p>
|
28
|
+
</div>
|
29
|
+
<div class="card-footer bg-transparent">
|
30
|
+
<div class="btn-group w-100" role="group">
|
31
|
+
<%= link_to "View", helpers.ragdoll.document_path(document), class: "btn btn-outline-primary btn-sm" %>
|
32
|
+
<%= link_to "Edit", helpers.ragdoll.edit_document_path(document), class: "btn btn-outline-secondary btn-sm" %>
|
33
|
+
<%= link_to "Delete", helpers.ragdoll.document_path(document), method: :delete,
|
34
|
+
class: "btn btn-outline-danger btn-sm",
|
35
|
+
data: { confirm: "Are you sure you want to delete '#{document.title}'?", turbo_method: :delete } %>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
<% end %>
|
41
|
+
</div>
|
@@ -0,0 +1,76 @@
|
|
1
|
+
<div class="table-responsive">
|
2
|
+
<table class="table table-hover">
|
3
|
+
<thead>
|
4
|
+
<tr>
|
5
|
+
<th width="30">
|
6
|
+
<input type="checkbox" id="select-all-table" class="form-check-input" onclick="toggleAll(this)">
|
7
|
+
</th>
|
8
|
+
<th>Title</th>
|
9
|
+
<th>Type</th>
|
10
|
+
<th>Size</th>
|
11
|
+
<th>Status</th>
|
12
|
+
<th>Created</th>
|
13
|
+
<th width="150">Actions</th>
|
14
|
+
</tr>
|
15
|
+
</thead>
|
16
|
+
<tbody>
|
17
|
+
<% documents.each do |document| %>
|
18
|
+
<tr>
|
19
|
+
<td>
|
20
|
+
<input type="checkbox" class="document-checkbox form-check-input" value="<%= document.id %>">
|
21
|
+
</td>
|
22
|
+
<td>
|
23
|
+
<%= link_to document.title, helpers.ragdoll.document_path(document), class: "text-decoration-none" %>
|
24
|
+
</td>
|
25
|
+
<td>
|
26
|
+
<small class="text-muted">
|
27
|
+
<i class="fas fa-file"></i> <%= document.document_type %>
|
28
|
+
</small>
|
29
|
+
</td>
|
30
|
+
<td>
|
31
|
+
<small class="text-muted">
|
32
|
+
<%= number_to_human_size(document.metadata['file_size']) if document.metadata && document.metadata['file_size'] %>
|
33
|
+
</small>
|
34
|
+
</td>
|
35
|
+
<td>
|
36
|
+
<span class="badge bg-<%= document.status == 'processed' ? 'success' : document.status == 'failed' ? 'danger' : 'warning' %>">
|
37
|
+
<%= document.status.humanize %>
|
38
|
+
</span>
|
39
|
+
</td>
|
40
|
+
<td>
|
41
|
+
<small class="text-muted">
|
42
|
+
<%= time_ago_in_words(document.created_at) %> ago
|
43
|
+
</small>
|
44
|
+
</td>
|
45
|
+
<td>
|
46
|
+
<div class="btn-group btn-group-sm" role="group">
|
47
|
+
<%= link_to helpers.ragdoll.document_path(document),
|
48
|
+
class: "btn btn-outline-primary",
|
49
|
+
title: "View",
|
50
|
+
data: { bs_toggle: "tooltip" } do %>
|
51
|
+
<i class="fas fa-eye"></i>
|
52
|
+
<% end %>
|
53
|
+
<%= link_to helpers.ragdoll.edit_document_path(document),
|
54
|
+
class: "btn btn-outline-secondary",
|
55
|
+
title: "Edit",
|
56
|
+
data: { bs_toggle: "tooltip" } do %>
|
57
|
+
<i class="fas fa-edit"></i>
|
58
|
+
<% end %>
|
59
|
+
<%= link_to helpers.ragdoll.document_path(document),
|
60
|
+
method: :delete,
|
61
|
+
class: "btn btn-outline-danger",
|
62
|
+
title: "Delete",
|
63
|
+
data: {
|
64
|
+
confirm: "Are you sure you want to delete '#{document.title}'?",
|
65
|
+
turbo_method: :delete,
|
66
|
+
bs_toggle: "tooltip"
|
67
|
+
} do %>
|
68
|
+
<i class="fas fa-trash"></i>
|
69
|
+
<% end %>
|
70
|
+
</div>
|
71
|
+
</td>
|
72
|
+
</tr>
|
73
|
+
<% end %>
|
74
|
+
</tbody>
|
75
|
+
</table>
|
76
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div class="text-center py-5">
|
2
|
+
<% if icon %>
|
3
|
+
<i class="<%= icon %> fa-4x text-muted mb-4"></i>
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<h3 class="text-muted mb-3"><%= title %></h3>
|
7
|
+
<p class="text-muted mb-4"><%= message %></p>
|
8
|
+
|
9
|
+
<% if action_path && action_text %>
|
10
|
+
<%= link_to action_text, action_path, class: "btn btn-primary" %>
|
11
|
+
<% end %>
|
12
|
+
</div>
|