solid_bro 0.1.2

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.
@@ -0,0 +1,636 @@
1
+ :root {
2
+ --bg-gradient-start: #0f2027;
3
+ --bg-gradient-mid: #203a43;
4
+ --bg-gradient-end: #2c5364;
5
+
6
+ --bg-gradient: linear-gradient(135deg, var(--bg-gradient-start), var(--bg-gradient-mid), var(--bg-gradient-end));
7
+
8
+ --surface-color: rgba(255, 255, 255, 0.05);
9
+ --surface-border: rgba(255, 255, 255, 0.1);
10
+ --text-primary: #ffffff;
11
+ --text-secondary: #a0a0a0;
12
+
13
+ --accent-color: #00d2ff;
14
+ --accent-hover: #3a7bd5;
15
+ --info-color: #3a7bd5;
16
+ --danger-color: #ff416c;
17
+ --success-color: #00b09b;
18
+ --warning-color: #f7b733;
19
+
20
+ --font-family: 'Inter', sans-serif;
21
+ }
22
+
23
+ * {
24
+ box-sizing: border-box;
25
+ margin: 0;
26
+ padding: 0;
27
+ }
28
+
29
+ body {
30
+ font-family: var(--font-family);
31
+ background: var(--bg-gradient);
32
+ color: var(--text-primary);
33
+ min-height: 100vh;
34
+ display: flex;
35
+ flex-direction: column;
36
+ }
37
+
38
+ /* Header */
39
+ .top-menu {
40
+ display: flex;
41
+ justify-content: space-between;
42
+ align-items: center;
43
+ padding: 1rem 2rem;
44
+ background: rgba(0, 0, 0, 0.2);
45
+ backdrop-filter: blur(10px);
46
+ border-bottom: 1px solid var(--surface-border);
47
+ }
48
+
49
+ .logo {
50
+ font-size: 1.5rem;
51
+ font-weight: 700;
52
+ color: var(--accent-color);
53
+ letter-spacing: -0.5px;
54
+ }
55
+
56
+ .top-menu nav ul {
57
+ list-style: none;
58
+ display: flex;
59
+ gap: 2rem;
60
+ }
61
+
62
+ .top-menu nav a {
63
+ color: var(--text-secondary);
64
+ text-decoration: none;
65
+ font-weight: 600;
66
+ transition: color 0.3s;
67
+ }
68
+
69
+ .top-menu nav a:hover,
70
+ .top-menu nav a.active {
71
+ color: var(--text-primary);
72
+ }
73
+
74
+ .user-profile .avatar {
75
+ width: 40px;
76
+ height: 40px;
77
+ background: var(--accent-color);
78
+ color: #fff;
79
+ border-radius: 50%;
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: center;
83
+ font-weight: bold;
84
+ cursor: pointer;
85
+ }
86
+
87
+ /* Main Content */
88
+ .content {
89
+ flex: 1;
90
+ padding: 2rem;
91
+ max-width: 1200px;
92
+ margin: 0 auto;
93
+ width: 100%;
94
+ }
95
+
96
+ .table-container {
97
+ background: var(--surface-color);
98
+ border-radius: 12px;
99
+ border: 1px solid var(--surface-border);
100
+ padding: 1.5rem;
101
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
102
+ }
103
+
104
+ .table-header {
105
+ display: flex;
106
+ justify-content: space-between;
107
+ align-items: center;
108
+ margin-bottom: 1.5rem;
109
+ }
110
+
111
+ .table-header h2 {
112
+ font-size: 1.25rem;
113
+ font-weight: 600;
114
+ }
115
+
116
+ table {
117
+ width: 100%;
118
+ border-collapse: separate;
119
+ border-spacing: 0;
120
+ }
121
+
122
+ th,
123
+ td {
124
+ padding: 1rem;
125
+ text-align: left;
126
+ border-bottom: 1px solid var(--surface-border);
127
+ }
128
+
129
+ th {
130
+ color: var(--text-secondary);
131
+ font-weight: 600;
132
+ font-size: 0.875rem;
133
+ text-transform: uppercase;
134
+ letter-spacing: 0.05em;
135
+ }
136
+
137
+ td {
138
+ font-size: 0.95rem;
139
+ color: var(--text-primary);
140
+ }
141
+
142
+ tr:last-child td {
143
+ border-bottom: none;
144
+ }
145
+
146
+ tr:hover td {
147
+ background: rgba(255, 255, 255, 0.02);
148
+ }
149
+
150
+ /* Utilities & Components */
151
+ .text-right {
152
+ text-align: right;
153
+ }
154
+
155
+ .btn-primary {
156
+ background: var(--accent-color);
157
+ color: #000;
158
+ border: none;
159
+ padding: 0.6rem 1.2rem;
160
+ border-radius: 6px;
161
+ font-weight: 600;
162
+ cursor: pointer;
163
+ transition: background 0.2s, transform 0.1s;
164
+ }
165
+
166
+ .btn-primary:hover {
167
+ background: var(--accent-hover);
168
+ color: #fff;
169
+ }
170
+
171
+ .btn-primary:active {
172
+ transform: translateY(1px);
173
+ }
174
+
175
+ .btn-icon {
176
+ background: transparent;
177
+ border: 1px solid var(--surface-border);
178
+ color: var(--text-secondary);
179
+ padding: 0.4rem 0.8rem;
180
+ border-radius: 4px;
181
+ cursor: pointer;
182
+ font-size: 0.85rem;
183
+ margin-left: 0.5rem;
184
+ transition: all 0.2s;
185
+ }
186
+
187
+ .btn-icon:hover {
188
+ background: rgba(255, 255, 255, 0.1);
189
+ color: var(--text-primary);
190
+ }
191
+
192
+ .btn-danger:hover {
193
+ border-color: var(--danger-color);
194
+ color: var(--danger-color);
195
+ }
196
+
197
+ .job-action-buttons {
198
+ display: inline-flex;
199
+ align-items: center;
200
+ justify-content: flex-end;
201
+ gap: 0.4rem;
202
+ }
203
+
204
+ .job-action-buttons .btn-icon {
205
+ display: inline-flex;
206
+ align-items: center;
207
+ justify-content: center;
208
+ width: 2.25rem;
209
+ height: 2.25rem;
210
+ padding: 0;
211
+ margin-left: 0;
212
+ }
213
+
214
+ .queue-action-buttons {
215
+ display: inline-flex;
216
+ align-items: center;
217
+ justify-content: flex-end;
218
+ gap: 0.5rem;
219
+ }
220
+
221
+ .queue-action-buttons .btn-icon {
222
+ display: inline-flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ min-width: 6rem;
226
+ padding: 0.4rem 0.9rem;
227
+ margin-left: 0;
228
+ }
229
+
230
+ .job-action-icon {
231
+ width: 1rem;
232
+ height: 1rem;
233
+ }
234
+
235
+ .badge {
236
+ padding: 0.25rem 0.6rem;
237
+ border-radius: 20px;
238
+ font-size: 0.75rem;
239
+ font-weight: 600;
240
+ }
241
+
242
+ .badge-admin {
243
+ background: rgba(247, 183, 51, 0.15);
244
+ color: var(--warning-color);
245
+ }
246
+
247
+ .badge-editor {
248
+ background: rgba(0, 210, 255, 0.15);
249
+ color: var(--accent-color);
250
+ }
251
+
252
+ .badge-viewer {
253
+ background: rgba(255, 255, 255, 0.1);
254
+ color: var(--text-secondary);
255
+ }
256
+
257
+ .status-dot {
258
+ display: inline-block;
259
+ width: 6px;
260
+ height: 6px;
261
+ border-radius: 50%;
262
+ margin-right: 6px;
263
+ vertical-align: middle;
264
+ }
265
+
266
+ .status-active {
267
+ background-color: var(--success-color);
268
+ box-shadow: 0 0 8px var(--success-color);
269
+ }
270
+
271
+ .status-offline {
272
+ background-color: var(--text-secondary);
273
+ }
274
+
275
+ /* Footer */
276
+ .footer {
277
+ position: fixed;
278
+ bottom: 0;
279
+ left: 0;
280
+ width: 100%;
281
+ background: rgba(0, 0, 0, 0.2);
282
+ backdrop-filter: blur(10px);
283
+ -webkit-backdrop-filter: blur(10px);
284
+ border-top: 1px solid var(--surface-border);
285
+ padding: 1rem 0;
286
+ z-index: 1000;
287
+ }
288
+
289
+ .footer-content {
290
+ max-width: 1200px;
291
+ margin: 0 auto;
292
+ padding: 0 2rem;
293
+ display: flex;
294
+ justify-content: space-between;
295
+ align-items: center;
296
+ }
297
+
298
+ .copyright {
299
+ color: var(--text-secondary);
300
+ font-size: 0.9rem;
301
+ }
302
+
303
+ .footer-links {
304
+ display: flex;
305
+ gap: 1.5rem;
306
+ }
307
+
308
+ .footer-links a {
309
+ color: var(--text-secondary);
310
+ text-decoration: none;
311
+ font-size: 0.9rem;
312
+ transition: color 0.3s ease;
313
+ }
314
+
315
+ .footer-links a:hover {
316
+ color: var(--text-primary);
317
+ text-decoration: underline;
318
+ }
319
+
320
+ /* Adjust content to prevent overlapping with fixed footer */
321
+ body {
322
+ padding-bottom: 60px;
323
+ /* Approximate height of the footer */
324
+ }
325
+
326
+ /* Pagination (Pagy) – matches site look */
327
+ .pagination-wrapper {
328
+ margin-top: 1.5rem;
329
+ padding-top: 1rem;
330
+ border-top: 1px solid var(--surface-border);
331
+ }
332
+
333
+ /* Pagination (Pagy 43 uses class "pagy series-nav") */
334
+ .pagy.series-nav {
335
+ display: flex;
336
+ flex-wrap: wrap;
337
+ align-items: center;
338
+ gap: 0.5rem;
339
+ font-family: var(--font-family);
340
+ font-size: 0.875rem;
341
+ line-height: 1.25rem;
342
+ font-weight: 600;
343
+ color: var(--text-secondary);
344
+ }
345
+
346
+ .pagy.series-nav a {
347
+ display: block;
348
+ text-decoration: none;
349
+ border-radius: 6px;
350
+ background: rgba(255, 255, 255, 0.08);
351
+ border: 1px solid var(--surface-border);
352
+ padding: 0.4rem 0.75rem;
353
+ color: var(--text-secondary);
354
+ transition: background 0.2s, border-color 0.2s, color 0.2s;
355
+ }
356
+
357
+ .pagy.series-nav a[href]:hover {
358
+ background: rgba(255, 255, 255, 0.12);
359
+ border-color: var(--accent-color);
360
+ color: var(--text-primary);
361
+ }
362
+
363
+ .pagy.series-nav a[aria-disabled="true"]:not([aria-current="page"]) {
364
+ cursor: default;
365
+ background: rgba(255, 255, 255, 0.03);
366
+ border-color: var(--surface-border);
367
+ color: var(--text-secondary);
368
+ opacity: 0.6;
369
+ }
370
+
371
+ .pagy.series-nav a[aria-current="page"] {
372
+ background: var(--accent-color);
373
+ border-color: var(--accent-color);
374
+ color: #000;
375
+ }
376
+
377
+ .pagy.series-nav a[role="separator"] {
378
+ cursor: default;
379
+ background: transparent;
380
+ border-color: transparent;
381
+ padding: 0 0.25rem;
382
+ user-select: none;
383
+ }
384
+
385
+ /* Jobs index: tabs and filters */
386
+ .jobs-tabs {
387
+ margin-bottom: 1rem;
388
+ }
389
+
390
+ .jobs-tabs-list {
391
+ list-style: none;
392
+ display: flex;
393
+ flex-wrap: wrap;
394
+ gap: 0.25rem;
395
+ border-bottom: 1px solid var(--surface-border);
396
+ padding-bottom: 0;
397
+ }
398
+
399
+ .jobs-tabs-list li a {
400
+ display: block;
401
+ padding: 0.75rem 1rem;
402
+ color: var(--text-secondary);
403
+ text-decoration: none;
404
+ font-weight: 600;
405
+ font-size: 0.875rem;
406
+ border-bottom: 2px solid transparent;
407
+ margin-bottom: -1px;
408
+ transition: color 0.2s, border-color 0.2s;
409
+ }
410
+
411
+ .jobs-tabs-list li a:hover {
412
+ color: var(--text-primary);
413
+ }
414
+
415
+ .jobs-tabs-list li a.active {
416
+ color: var(--accent-color);
417
+ border-bottom-color: var(--accent-color);
418
+ }
419
+
420
+ .jobs-filters {
421
+ background: var(--surface-color);
422
+ border: 1px solid var(--surface-border);
423
+ border-radius: 12px;
424
+ padding: 1.25rem;
425
+ margin-bottom: 1.5rem;
426
+ }
427
+
428
+ .jobs-filters-grid {
429
+ display: grid;
430
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
431
+ gap: 1rem;
432
+ align-items: end;
433
+ }
434
+
435
+ .jobs-filter-field {
436
+ display: flex;
437
+ flex-direction: column;
438
+ gap: 0.35rem;
439
+ }
440
+
441
+ .jobs-filter-field label {
442
+ font-size: 0.8125rem;
443
+ font-weight: 600;
444
+ color: var(--text-secondary);
445
+ }
446
+
447
+ .jobs-filter-input {
448
+ padding: 0.5rem 0.75rem;
449
+ font-size: 0.875rem;
450
+ color: var(--text-primary);
451
+ background: rgba(0, 0, 0, 0.2);
452
+ border: 1px solid var(--surface-border);
453
+ border-radius: 6px;
454
+ font-family: inherit;
455
+ }
456
+
457
+ .jobs-filter-input::placeholder {
458
+ color: var(--text-secondary);
459
+ opacity: 0.8;
460
+ }
461
+
462
+ .jobs-filter-input:focus {
463
+ outline: none;
464
+ border-color: var(--accent-color);
465
+ }
466
+
467
+ .jobs-filter-actions {
468
+ display: flex;
469
+ gap: 0.5rem;
470
+ align-items: flex-end;
471
+ }
472
+
473
+ .link-accent {
474
+ color: var(--accent-color);
475
+ text-decoration: none;
476
+ }
477
+
478
+ .link-accent:hover {
479
+ color: var(--accent-hover);
480
+ text-decoration: underline;
481
+ }
482
+
483
+ .text-muted,
484
+ .text-secondary {
485
+ color: var(--text-secondary);
486
+ }
487
+
488
+ .text-xs {
489
+ font-size: 0.75rem;
490
+ }
491
+
492
+ .truncate {
493
+ overflow: hidden;
494
+ text-overflow: ellipsis;
495
+ white-space: nowrap;
496
+ }
497
+
498
+ .max-w-xs {
499
+ max-width: 20rem;
500
+ }
501
+
502
+ .code-inline {
503
+ font-family: ui-monospace, monospace;
504
+ font-size: 0.875em;
505
+ padding: 0.1em 0.35em;
506
+ background: rgba(0, 0, 0, 0.2);
507
+ border-radius: 4px;
508
+ }
509
+
510
+ /* Job show page */
511
+ .job-page-header {
512
+ display: flex;
513
+ flex-wrap: wrap;
514
+ justify-content: space-between;
515
+ align-items: flex-start;
516
+ gap: 1rem;
517
+ margin-bottom: 1.5rem;
518
+ }
519
+
520
+ .job-page-title h1 {
521
+ font-size: 1.75rem;
522
+ font-weight: 700;
523
+ color: var(--text-primary);
524
+ margin-bottom: 0.25rem;
525
+ }
526
+
527
+ .job-page-subtitle {
528
+ font-size: 0.95rem;
529
+ color: var(--text-secondary);
530
+ }
531
+
532
+ .job-queue-name {
533
+ color: var(--accent-color);
534
+ }
535
+
536
+ .job-page-actions {
537
+ display: flex;
538
+ gap: 0.5rem;
539
+ flex-shrink: 0;
540
+ }
541
+
542
+ /* Details table: two-column layout (label | value) */
543
+ .job-details-table tbody th {
544
+ width: 10rem;
545
+ min-width: 8rem;
546
+ font-weight: 600;
547
+ font-size: 0.875rem;
548
+ color: var(--text-secondary);
549
+ text-transform: none;
550
+ letter-spacing: 0;
551
+ vertical-align: top;
552
+ padding-top: 1rem;
553
+ padding-bottom: 1rem;
554
+ }
555
+
556
+ .job-details-table tbody td {
557
+ vertical-align: top;
558
+ }
559
+
560
+ /* Formatted raw data block */
561
+ .job-arguments-block {
562
+ margin: 0;
563
+ padding: 1rem;
564
+ font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace;
565
+ font-size: 0.8125rem;
566
+ line-height: 1.5;
567
+ color: var(--text-primary);
568
+ background: rgba(0, 0, 0, 0.25);
569
+ border: 1px solid var(--surface-border);
570
+ border-radius: 6px;
571
+ overflow-x: auto;
572
+ white-space: pre-wrap;
573
+ word-break: break-word;
574
+ }
575
+
576
+ .job-arguments-block--backtrace {
577
+ max-height: 20rem;
578
+ overflow-y: auto;
579
+ font-size: 0.75rem;
580
+ color: var(--text-secondary);
581
+ }
582
+
583
+ /* Status badges (shared with index) */
584
+ .status-badge {
585
+ display: inline-flex;
586
+ align-items: center;
587
+ padding: 0.25rem 0.6rem;
588
+ font-size: 0.75rem;
589
+ font-weight: 600;
590
+ border-radius: 9999px;
591
+ }
592
+
593
+ .status-ready {
594
+ background: rgba(0, 210, 255, 0.15);
595
+ color: var(--accent-color);
596
+ }
597
+
598
+ .status-claimed {
599
+ background: rgba(58, 123, 213, 0.2);
600
+ color: var(--info-color);
601
+ }
602
+
603
+ .status-finished {
604
+ background: rgba(0, 176, 155, 0.2);
605
+ color: var(--success-color);
606
+ }
607
+
608
+ .status-failed {
609
+ background: rgba(255, 65, 108, 0.2);
610
+ color: var(--danger-color);
611
+ }
612
+
613
+ .status-default {
614
+ background: rgba(255, 255, 255, 0.1);
615
+ color: var(--text-secondary);
616
+ }
617
+
618
+ /* Exception block */
619
+ .table-container--error {
620
+ border-color: rgba(255, 65, 108, 0.3);
621
+ background: rgba(255, 65, 108, 0.05);
622
+ }
623
+
624
+ .table-container--error .table-header h2 {
625
+ color: var(--danger-color);
626
+ }
627
+
628
+ .exception-content {
629
+ padding: 0 1.5rem 1.5rem;
630
+ }
631
+
632
+ .exception-message {
633
+ font-weight: 600;
634
+ color: var(--danger-color);
635
+ margin-bottom: 1rem;
636
+ }
File without changes
@@ -0,0 +1,29 @@
1
+ module SolidBro
2
+ class ApplicationController < ActionController::Base
3
+ # Support both Pagy 9.x (Backend) and Pagy 8+/43.x (Method)
4
+ if defined?(Pagy::Backend)
5
+ include Pagy::Backend
6
+ elsif defined?(Pagy::Method)
7
+ include Pagy::Method
8
+ end
9
+
10
+ # Optional Pundit integration (only if the host app uses Pundit)
11
+ if defined?(Pundit::Authorization)
12
+ include Pundit::Authorization
13
+
14
+ before_action :authorize_solid_bro!
15
+
16
+ private
17
+
18
+ # Expects a SolidBroPolicy in the host app:
19
+ # class SolidBroPolicy < Struct.new(:user, :solid_bro)
20
+ # def access?; user&.admin?; end
21
+ # end
22
+ def authorize_solid_bro!
23
+ authorize :solid_bro, :access?
24
+ end
25
+ end
26
+
27
+ protect_from_forgery with: :exception
28
+ end
29
+ end