solid_queue_dashboard 0.1.0 â 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Procfile.dev +2 -1
- data/README.md +13 -9
- data/app/assets/javascripts/solid_queue_dashboard/application.js +17 -3
- data/app/assets/stylesheets/solid_queue_dashboard/application.css +96 -24
- data/app/assets/stylesheets/solid_queue_dashboard/tailwind.css +5 -5
- data/app/controllers/solid_queue_dashboard/appearance_controller.rb +1 -1
- data/app/controllers/solid_queue_dashboard/dashboard_controller.rb +61 -1
- data/app/controllers/solid_queue_dashboard/jobs_controller.rb +1 -1
- data/app/controllers/solid_queue_dashboard/stats_controller.rb +8 -0
- data/app/helpers/solid_queue_dashboard/icons_helper.rb +23 -0
- data/app/helpers/solid_queue_dashboard/jobs_helper.rb +8 -2
- data/app/helpers/solid_queue_dashboard/pagination_helper.rb +2 -2
- data/app/helpers/solid_queue_dashboard/processes_helper.rb +4 -2
- data/app/views/layouts/solid_queue_dashboard/application.html.erb +5 -3
- data/app/views/solid_queue_dashboard/application/_navbar.html.erb +28 -5
- data/app/views/solid_queue_dashboard/dashboard/index.html.erb +16 -28
- data/app/views/solid_queue_dashboard/jobs/_table.html.erb +1 -1
- data/app/views/solid_queue_dashboard/jobs/_table_row.html.erb +19 -3
- data/app/views/solid_queue_dashboard/jobs/index.html.erb +5 -5
- data/app/views/solid_queue_dashboard/jobs/show.html.erb +5 -3
- data/app/views/solid_queue_dashboard/processes/_table_row.html.erb +6 -0
- data/app/views/solid_queue_dashboard/processes/show.html.erb +14 -1
- data/app/views/solid_queue_dashboard/recurring_tasks/show.html.erb +6 -1
- data/app/views/solid_queue_dashboard/stats/index.html.erb +25 -0
- data/config/routes.rb +2 -0
- data/lib/solid_queue_dashboard/decorators/job_decorator.rb +9 -2
- data/lib/solid_queue_dashboard/decorators/jobs_decorator.rb +7 -0
- data/lib/solid_queue_dashboard/job.rb +5 -3
- data/lib/solid_queue_dashboard/process.rb +3 -3
- data/lib/solid_queue_dashboard/version.rb +1 -1
- data/lib/solid_queue_dashboard.rb +2 -0
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6d60665de93e100be378a8e607aaf251e62bf7c991051633dd1aba8d9b2a84f
|
4
|
+
data.tar.gz: 47a0d627dbf19a6be53260c846ca3baa35038f64bb7d6e844d77a1de68e62c83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0dc95cf029ba6e32480b9a10c07d2f50106f0630b66f0385134181d84b1af1bfd4e0dff016f1d140a696aae180eaae83f173c6575e51a04a68bf7574f9177307
|
7
|
+
data.tar.gz: 5fdb3e8c5b8f08f3afb4769855595ccf2a8426ac429428f19bd32d82cec932d7882c892b60eb5f2e2ae8ae65822a91fc1ef86cc7701b6fa994fffe31912ddabe
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.2.0] - October 17, 2024
|
4
|
+
|
5
|
+
- Show running jobs
|
6
|
+
- Add charts
|
7
|
+
- Add auto-refresh
|
8
|
+
- Add number of current processes and recurring tasks to navbar
|
9
|
+
- Fix dark mode not switching for the first time
|
10
|
+
|
11
|
+
## [0.1.1] - October 7, 2024
|
12
|
+
|
13
|
+
- Replace OpenStruct with a Hash to avoid including the `ostruct` gem
|
14
|
+
|
3
15
|
## [0.1.0] - October 7, 2024
|
4
16
|
|
5
17
|
- Initial release
|
data/Procfile.dev
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
# Solid Queue Dashboard
|
1
|
+
# Solid Queue Dashboard <sup>BETA</sup>
|
2
2
|
|
3
3
|
<p align="center">
|
4
4
|
<a href="https://github.com/akodkod/solid-queue-dashboard#gh-light-mode-only">
|
5
|
-
<img src="https://github.com/user-attachments/assets/
|
5
|
+
<img src="https://github.com/user-attachments/assets/55aa4a3c-da51-471b-8f58-0cf1f9a1f8da" alt="Solid Queue Dashboard Light Mode">
|
6
6
|
</a>
|
7
7
|
<a href="https://github.com/akodkod/solid-queue-dashboard#gh-dark-mode-only">
|
8
|
-
<img src="https://github.com/user-attachments/assets/
|
8
|
+
<img src="https://github.com/user-attachments/assets/645558cb-c20f-4d4b-9697-55282710ea6c" alt="Solid Queue Dashboard Dark Mode">
|
9
9
|
</a>
|
10
10
|
|
11
|
-
_đ I'm
|
11
|
+
_đ I'm Available for Hire â [kodkod.me](https://kodkod.me)_
|
12
12
|
</p>
|
13
13
|
|
14
14
|
## Features
|
@@ -19,12 +19,12 @@
|
|
19
19
|
- đ View execution history
|
20
20
|
- đ Filter options
|
21
21
|
- đ Retry jobs from the UI
|
22
|
-
-
|
22
|
+
- đĨŦ Auto-refresh
|
23
|
+
- đ Add charts
|
23
24
|
- đ No monkey patching
|
25
|
+
- đ TailwindCSS
|
24
26
|
|
25
27
|
## Roadmap
|
26
|
-
- đââī¸ Auto-pooling
|
27
|
-
- đ Add charts
|
28
28
|
- đ Manually trigger jobs
|
29
29
|
- âšī¸ Cancel long jobs (if possible)
|
30
30
|
- đ More statistics and insights
|
@@ -43,7 +43,7 @@ bundle add solid_queue_dashboard
|
|
43
43
|
Or add this line to your `Gemfile`:
|
44
44
|
|
45
45
|
```bash
|
46
|
-
gem "solid_queue_dashboard", "~> 0.
|
46
|
+
gem "solid_queue_dashboard", "~> 0.2.0"
|
47
47
|
```
|
48
48
|
|
49
49
|
Add this line to `routes.rb`:
|
@@ -85,10 +85,14 @@ To generate dummy data:
|
|
85
85
|
```
|
86
86
|
cd test_app
|
87
87
|
rails jobs:generate_dummy_data
|
88
|
-
./bin/jobs
|
89
88
|
```
|
90
89
|
|
91
90
|
## License
|
92
91
|
|
93
92
|
This gem is open source under the [MIT License](http://opensource.org/licenses/MIT).
|
94
93
|
|
94
|
+
---
|
95
|
+
|
96
|
+
_Made with love by Ukrainians đđ_
|
97
|
+
_[Help Ukraine](https://u24.gov.ua/)_
|
98
|
+
|
@@ -16,7 +16,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
16
16
|
});
|
17
17
|
|
18
18
|
// Format dates
|
19
|
-
document.querySelectorAll('[data-date]').forEach(function(element) {
|
19
|
+
document.querySelectorAll('[data-date]').forEach(function (element) {
|
20
20
|
const dateString = element.textContent.trim();
|
21
21
|
const date = new Date(dateString);
|
22
22
|
|
@@ -37,10 +37,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
37
37
|
});
|
38
38
|
|
39
39
|
// Handle auto-submit elements
|
40
|
-
document.querySelectorAll('[data-auto-submit]').forEach(function(element) {
|
41
|
-
element.addEventListener('change', function() {
|
40
|
+
document.querySelectorAll('[data-auto-submit]').forEach(function (element) {
|
41
|
+
element.addEventListener('change', function () {
|
42
42
|
const form = element.closest('form');
|
43
43
|
if (form) form.submit();
|
44
44
|
});
|
45
45
|
});
|
46
|
+
|
47
|
+
|
48
|
+
// Auto-refresh functionality for dashboard page
|
49
|
+
const searchParams = new URLSearchParams(window.location.search);
|
50
|
+
const refreshInterval = parseInt(searchParams.get('auto_refresh_period'));
|
51
|
+
|
52
|
+
if (refreshInterval) {
|
53
|
+
setInterval(refreshHomePage, refreshInterval * 1000);
|
54
|
+
}
|
55
|
+
|
56
|
+
function refreshHomePage() {
|
57
|
+
// TODO: Implement a smart refresh strategy using Fetch or Turbo
|
58
|
+
window.location.reload();
|
59
|
+
}
|
46
60
|
});
|
@@ -584,12 +584,12 @@ video {
|
|
584
584
|
|
585
585
|
.dark {
|
586
586
|
--background: 240 10% 5.4%;
|
587
|
-
--foreground: 0 0%
|
587
|
+
--foreground: 0 0% 92%;
|
588
588
|
--card: 240 10% 3.9%;
|
589
|
-
--card-foreground: 0 0%
|
589
|
+
--card-foreground: 0 0% 92%;
|
590
590
|
--popover: 240 10% 3.9%;
|
591
591
|
--popover-foreground: 0 0% 98%;
|
592
|
-
--primary: 0 0%
|
592
|
+
--primary: 0 0% 95%;
|
593
593
|
--primary-foreground: 240 5.9% 10%;
|
594
594
|
--secondary: 240 3.7% 15.9%;
|
595
595
|
--secondary-foreground: 0 0% 98%;
|
@@ -796,6 +796,16 @@ body {
|
|
796
796
|
background-color: hsl(var(--primary) / 0.8);
|
797
797
|
}
|
798
798
|
|
799
|
+
.badge-secondary {
|
800
|
+
border-color: transparent;
|
801
|
+
background-color: hsl(var(--secondary));
|
802
|
+
color: hsl(var(--secondary-foreground));
|
803
|
+
}
|
804
|
+
|
805
|
+
.badge-secondary:hover {
|
806
|
+
background-color: hsl(var(--secondary) / 0.8);
|
807
|
+
}
|
808
|
+
|
799
809
|
.badge-destructive {
|
800
810
|
border-color: transparent;
|
801
811
|
background-color: hsl(var(--destructive) / 0.15);
|
@@ -901,6 +911,18 @@ body {
|
|
901
911
|
color: rgb(96 165 250 / var(--tw-text-opacity));
|
902
912
|
}
|
903
913
|
|
914
|
+
.badge-indigo {
|
915
|
+
border-color: transparent;
|
916
|
+
background-color: rgb(99 102 241 / 0.15);
|
917
|
+
--tw-text-opacity: 1;
|
918
|
+
color: rgb(67 56 202 / var(--tw-text-opacity));
|
919
|
+
}
|
920
|
+
|
921
|
+
.badge-indigo:is(.dark *) {
|
922
|
+
--tw-text-opacity: 1;
|
923
|
+
color: rgb(129 140 248 / var(--tw-text-opacity));
|
924
|
+
}
|
925
|
+
|
904
926
|
.badge-purple {
|
905
927
|
border-color: transparent;
|
906
928
|
background-color: rgb(168 85 247 / 0.15);
|
@@ -915,7 +937,7 @@ body {
|
|
915
937
|
|
916
938
|
.badge-zinc {
|
917
939
|
border-color: transparent;
|
918
|
-
background-color: rgb(82 82 91 / 0.
|
940
|
+
background-color: rgb(82 82 91 / 0.075);
|
919
941
|
--tw-text-opacity: 1;
|
920
942
|
color: rgb(63 63 70 / var(--tw-text-opacity));
|
921
943
|
}
|
@@ -969,6 +991,11 @@ body {
|
|
969
991
|
background-color: rgb(59 130 246 / var(--tw-bg-opacity));
|
970
992
|
}
|
971
993
|
|
994
|
+
.circle-indigo {
|
995
|
+
--tw-bg-opacity: 1;
|
996
|
+
background-color: rgb(99 102 241 / var(--tw-bg-opacity));
|
997
|
+
}
|
998
|
+
|
972
999
|
.circle-purple {
|
973
1000
|
--tw-bg-opacity: 1;
|
974
1001
|
background-color: rgb(168 85 247 / var(--tw-bg-opacity));
|
@@ -976,7 +1003,7 @@ body {
|
|
976
1003
|
|
977
1004
|
.circle-zinc {
|
978
1005
|
--tw-bg-opacity: 1;
|
979
|
-
background-color: rgb(
|
1006
|
+
background-color: rgb(161 161 170 / var(--tw-bg-opacity));
|
980
1007
|
}
|
981
1008
|
|
982
1009
|
/*
|
@@ -1504,6 +1531,22 @@ body {
|
|
1504
1531
|
position: static;
|
1505
1532
|
}
|
1506
1533
|
|
1534
|
+
.absolute {
|
1535
|
+
position: absolute;
|
1536
|
+
}
|
1537
|
+
|
1538
|
+
.relative {
|
1539
|
+
position: relative;
|
1540
|
+
}
|
1541
|
+
|
1542
|
+
.right-6 {
|
1543
|
+
right: 1.5rem;
|
1544
|
+
}
|
1545
|
+
|
1546
|
+
.top-6 {
|
1547
|
+
top: 1.5rem;
|
1548
|
+
}
|
1549
|
+
|
1507
1550
|
.mx-auto {
|
1508
1551
|
margin-left: auto;
|
1509
1552
|
margin-right: auto;
|
@@ -1529,6 +1572,10 @@ body {
|
|
1529
1572
|
margin-left: 0.125rem;
|
1530
1573
|
}
|
1531
1574
|
|
1575
|
+
.ml-1 {
|
1576
|
+
margin-left: 0.25rem;
|
1577
|
+
}
|
1578
|
+
|
1532
1579
|
.ml-1\.5 {
|
1533
1580
|
margin-left: 0.375rem;
|
1534
1581
|
}
|
@@ -1561,6 +1608,10 @@ body {
|
|
1561
1608
|
margin-right: 0.375rem;
|
1562
1609
|
}
|
1563
1610
|
|
1611
|
+
.mt-0\.5 {
|
1612
|
+
margin-top: 0.125rem;
|
1613
|
+
}
|
1614
|
+
|
1564
1615
|
.mt-1 {
|
1565
1616
|
margin-top: 0.25rem;
|
1566
1617
|
}
|
@@ -1581,10 +1632,6 @@ body {
|
|
1581
1632
|
margin-top: 2rem;
|
1582
1633
|
}
|
1583
1634
|
|
1584
|
-
.mt-0\.5 {
|
1585
|
-
margin-top: 0.125rem;
|
1586
|
-
}
|
1587
|
-
|
1588
1635
|
.inline-block {
|
1589
1636
|
display: inline-block;
|
1590
1637
|
}
|
@@ -1641,6 +1688,10 @@ body {
|
|
1641
1688
|
width: 6rem;
|
1642
1689
|
}
|
1643
1690
|
|
1691
|
+
.w-40 {
|
1692
|
+
width: 10rem;
|
1693
|
+
}
|
1694
|
+
|
1644
1695
|
.w-5 {
|
1645
1696
|
width: 1.25rem;
|
1646
1697
|
}
|
@@ -1667,8 +1718,8 @@ body {
|
|
1667
1718
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
1668
1719
|
}
|
1669
1720
|
|
1670
|
-
.grid-cols-
|
1671
|
-
grid-template-columns: repeat(
|
1721
|
+
.grid-cols-6 {
|
1722
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
1672
1723
|
}
|
1673
1724
|
|
1674
1725
|
.\!flex-row {
|
@@ -1683,6 +1734,10 @@ body {
|
|
1683
1734
|
flex-wrap: wrap;
|
1684
1735
|
}
|
1685
1736
|
|
1737
|
+
.items-start {
|
1738
|
+
align-items: flex-start;
|
1739
|
+
}
|
1740
|
+
|
1686
1741
|
.items-center {
|
1687
1742
|
align-items: center;
|
1688
1743
|
}
|
@@ -1703,6 +1758,10 @@ body {
|
|
1703
1758
|
gap: 0.25rem;
|
1704
1759
|
}
|
1705
1760
|
|
1761
|
+
.gap-1\.5 {
|
1762
|
+
gap: 0.375rem;
|
1763
|
+
}
|
1764
|
+
|
1706
1765
|
.gap-2 {
|
1707
1766
|
gap: 0.5rem;
|
1708
1767
|
}
|
@@ -1785,6 +1844,11 @@ body {
|
|
1785
1844
|
border-top-color: rgb(239 68 68 / var(--tw-border-opacity));
|
1786
1845
|
}
|
1787
1846
|
|
1847
|
+
.border-t-sky-500 {
|
1848
|
+
--tw-border-opacity: 1;
|
1849
|
+
border-top-color: rgb(14 165 233 / var(--tw-border-opacity));
|
1850
|
+
}
|
1851
|
+
|
1788
1852
|
.border-t-zinc-500 {
|
1789
1853
|
--tw-border-opacity: 1;
|
1790
1854
|
border-top-color: rgb(113 113 122 / var(--tw-border-opacity));
|
@@ -1798,6 +1862,14 @@ body {
|
|
1798
1862
|
padding: 0px !important;
|
1799
1863
|
}
|
1800
1864
|
|
1865
|
+
.p-0 {
|
1866
|
+
padding: 0px;
|
1867
|
+
}
|
1868
|
+
|
1869
|
+
.p-6 {
|
1870
|
+
padding: 1.5rem;
|
1871
|
+
}
|
1872
|
+
|
1801
1873
|
.px-2 {
|
1802
1874
|
padding-left: 0.5rem;
|
1803
1875
|
padding-right: 0.5rem;
|
@@ -1822,6 +1894,10 @@ body {
|
|
1822
1894
|
padding-bottom: 15vh;
|
1823
1895
|
}
|
1824
1896
|
|
1897
|
+
.pl-1 {
|
1898
|
+
padding-left: 0.25rem;
|
1899
|
+
}
|
1900
|
+
|
1825
1901
|
.pl-6 {
|
1826
1902
|
padding-left: 1.5rem;
|
1827
1903
|
}
|
@@ -1867,19 +1943,6 @@ body {
|
|
1867
1943
|
line-height: 1rem;
|
1868
1944
|
}
|
1869
1945
|
|
1870
|
-
.text-base {
|
1871
|
-
font-size: 1rem;
|
1872
|
-
line-height: 1.5rem;
|
1873
|
-
}
|
1874
|
-
|
1875
|
-
.text-\[15px\] {
|
1876
|
-
font-size: 15px;
|
1877
|
-
}
|
1878
|
-
|
1879
|
-
.text-\[14px\] {
|
1880
|
-
font-size: 14px;
|
1881
|
-
}
|
1882
|
-
|
1883
1946
|
.font-bold {
|
1884
1947
|
font-weight: 700;
|
1885
1948
|
}
|
@@ -1964,6 +2027,10 @@ body {
|
|
1964
2027
|
}
|
1965
2028
|
}
|
1966
2029
|
|
2030
|
+
.running {
|
2031
|
+
animation-play-state: running;
|
2032
|
+
}
|
2033
|
+
|
1967
2034
|
.marker\:text-sm *::marker {
|
1968
2035
|
font-size: 0.875rem;
|
1969
2036
|
line-height: 1.25rem;
|
@@ -2009,6 +2076,11 @@ body {
|
|
2009
2076
|
border-top-color: rgb(147 51 234 / var(--tw-border-opacity));
|
2010
2077
|
}
|
2011
2078
|
|
2079
|
+
.dark\:border-t-sky-600:is(.dark *) {
|
2080
|
+
--tw-border-opacity: 1;
|
2081
|
+
border-top-color: rgb(2 132 199 / var(--tw-border-opacity));
|
2082
|
+
}
|
2083
|
+
|
2012
2084
|
.dark\:border-t-zinc-600:is(.dark *) {
|
2013
2085
|
--tw-border-opacity: 1;
|
2014
2086
|
border-top-color: rgb(82 82 91 / var(--tw-border-opacity));
|
@@ -33,12 +33,12 @@
|
|
33
33
|
|
34
34
|
.dark {
|
35
35
|
--background: 240 10% 5.4%;
|
36
|
-
--foreground: 0 0%
|
36
|
+
--foreground: 0 0% 92%;
|
37
37
|
--card: 240 10% 3.9%;
|
38
|
-
--card-foreground: 0 0%
|
38
|
+
--card-foreground: 0 0% 92%;
|
39
39
|
--popover: 240 10% 3.9%;
|
40
40
|
--popover-foreground: 0 0% 98%;
|
41
|
-
--primary: 0 0%
|
41
|
+
--primary: 0 0% 95%;
|
42
42
|
--primary-foreground: 240 5.9% 10%;
|
43
43
|
--secondary: 240 3.7% 15.9%;
|
44
44
|
--secondary-foreground: 0 0% 98%;
|
@@ -188,7 +188,7 @@
|
|
188
188
|
}
|
189
189
|
|
190
190
|
.badge-zinc {
|
191
|
-
@apply border-transparent bg-zinc-600/
|
191
|
+
@apply border-transparent bg-zinc-600/7.5 text-zinc-700 dark:bg-white/5 dark:text-zinc-400;
|
192
192
|
}
|
193
193
|
|
194
194
|
/*
|
@@ -284,7 +284,7 @@
|
|
284
284
|
}
|
285
285
|
|
286
286
|
.circle-zinc {
|
287
|
-
@apply bg-zinc-
|
287
|
+
@apply bg-zinc-400;
|
288
288
|
}
|
289
289
|
|
290
290
|
/*
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module SolidQueueDashboard
|
2
2
|
class AppearanceController < ApplicationController
|
3
3
|
def toggle
|
4
|
-
cookies[:dark_mode] = cookies[:dark_mode] == "
|
4
|
+
cookies[:dark_mode] = cookies[:dark_mode] == "true" ? "false" : "true"
|
5
5
|
redirect_to request.referer
|
6
6
|
end
|
7
7
|
end
|
@@ -2,7 +2,67 @@ module SolidQueueDashboard
|
|
2
2
|
class DashboardController < ApplicationController
|
3
3
|
def index
|
4
4
|
@jobs = SolidQueueDashboard.decorate(SolidQueue::Job.all)
|
5
|
-
|
5
|
+
load_charts
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def load_charts
|
11
|
+
case params[:chart_period] || "30m"
|
12
|
+
when "15m"
|
13
|
+
n = 1
|
14
|
+
last = 15
|
15
|
+
when "30m"
|
16
|
+
n = 1
|
17
|
+
last = 30
|
18
|
+
when "1h"
|
19
|
+
n = 2
|
20
|
+
last = 30
|
21
|
+
when "3h"
|
22
|
+
n = 6
|
23
|
+
last = 30
|
24
|
+
when "6h"
|
25
|
+
n = 12
|
26
|
+
last = 30
|
27
|
+
when "12h"
|
28
|
+
n = 20
|
29
|
+
last = 36
|
30
|
+
when "1d"
|
31
|
+
n = 30
|
32
|
+
last = 48
|
33
|
+
when "3d"
|
34
|
+
n = 90 # 1.5 hours
|
35
|
+
last = 48
|
36
|
+
when "1w"
|
37
|
+
n = 180 # 3 hours
|
38
|
+
last = 56
|
39
|
+
else
|
40
|
+
n = 1
|
41
|
+
last = 30
|
42
|
+
end
|
43
|
+
|
44
|
+
@charts = [
|
45
|
+
{
|
46
|
+
name: "Enqueued",
|
47
|
+
data: SolidQueue::Job.group_by_minute(:created_at, last: last, n: n).count,
|
48
|
+
color: "#A1A1AB"
|
49
|
+
},
|
50
|
+
{
|
51
|
+
name: "Finished",
|
52
|
+
data: @jobs.finished.group_by_minute(:finished_at, last: last, n: n).count,
|
53
|
+
color: "#23C55E"
|
54
|
+
},
|
55
|
+
{
|
56
|
+
name: "Retried",
|
57
|
+
data: @jobs.retried.group_by_minute(:finished_at, last: last, n: n).count,
|
58
|
+
color: "#FBBF26"
|
59
|
+
},
|
60
|
+
{
|
61
|
+
name: "Failed",
|
62
|
+
data: SolidQueue::FailedExecution.group_by_minute(:created_at, last: last, n: n).count,
|
63
|
+
color: "#F04444"
|
64
|
+
}
|
65
|
+
]
|
6
66
|
end
|
7
67
|
end
|
8
68
|
end
|
@@ -32,7 +32,7 @@ module SolidQueueDashboard
|
|
32
32
|
jobs = jobs.where(queue_name: params[:queue_name]) if params[:queue_name].present?
|
33
33
|
|
34
34
|
@pagination = paginate(jobs, page: params[:page].to_i, per_page: params[:per_page].to_i)
|
35
|
-
@jobs = SolidQueueDashboard.decorate(@pagination
|
35
|
+
@jobs = SolidQueueDashboard.decorate(@pagination[:records])
|
36
36
|
end
|
37
37
|
|
38
38
|
def set_job
|
@@ -227,5 +227,28 @@ module SolidQueueDashboard
|
|
227
227
|
concat tag.polygon(points: "6 3 20 12 6 21 6 3")
|
228
228
|
end
|
229
229
|
end
|
230
|
+
|
231
|
+
def icon_chart_scatter(options = {})
|
232
|
+
svg_options = {
|
233
|
+
xmlns: "http://www.w3.org/2000/svg",
|
234
|
+
width: "24",
|
235
|
+
height: "24",
|
236
|
+
viewBox: "0 0 24 24",
|
237
|
+
fill: "none",
|
238
|
+
stroke: "currentColor",
|
239
|
+
"stroke-width": "2",
|
240
|
+
"stroke-linecap": "round",
|
241
|
+
"stroke-linejoin": "round"
|
242
|
+
}.merge(options)
|
243
|
+
|
244
|
+
tag.svg(**svg_options) do
|
245
|
+
concat tag.circle(cx: "7.5", cy: "7.5", r: ".5", fill: "currentColor")
|
246
|
+
concat tag.circle(cx: "18.5", cy: "5.5", r: ".5", fill: "currentColor")
|
247
|
+
concat tag.circle(cx: "11.5", cy: "11.5", r: ".5", fill: "currentColor")
|
248
|
+
concat tag.circle(cx: "7.5", cy: "16.5", r: ".5", fill: "currentColor")
|
249
|
+
concat tag.circle(cx: "17.5", cy: "14.5", r: ".5", fill: "currentColor")
|
250
|
+
concat tag.path(d: "M3 3v16a2 2 0 0 0 2 2h16")
|
251
|
+
end
|
252
|
+
end
|
230
253
|
end
|
231
254
|
end
|
@@ -11,7 +11,10 @@ module SolidQueueDashboard
|
|
11
11
|
"amber": "circle-amber",
|
12
12
|
"red": "circle-red",
|
13
13
|
"blue": "circle-blue",
|
14
|
-
"
|
14
|
+
"sky": "circle-sky",
|
15
|
+
"zinc": "circle-zinc",
|
16
|
+
"indigo": "circle-indigo",
|
17
|
+
"purple": "circle-purple"
|
15
18
|
}[Job::STATUS_COLORS[status]&.to_sym || :zinc]
|
16
19
|
end
|
17
20
|
|
@@ -26,7 +29,10 @@ module SolidQueueDashboard
|
|
26
29
|
"amber": "badge-amber",
|
27
30
|
"red": "badge-red",
|
28
31
|
"blue": "badge-blue",
|
29
|
-
"
|
32
|
+
"sky": "badge-sky",
|
33
|
+
"zinc": "badge-zinc",
|
34
|
+
"indigo": "badge-indigo",
|
35
|
+
"purple": "badge-purple"
|
30
36
|
}[Job::STATUS_COLORS[status]&.to_sym || :zinc]
|
31
37
|
end
|
32
38
|
|
@@ -10,13 +10,13 @@ module SolidQueueDashboard
|
|
10
10
|
total_count = scope.count
|
11
11
|
total_pages = (total_count.to_f / per_page).ceil
|
12
12
|
|
13
|
-
|
13
|
+
{
|
14
14
|
records: records,
|
15
15
|
current_page: page,
|
16
16
|
per_page: per_page,
|
17
17
|
total_pages: total_pages,
|
18
18
|
total_count: total_count
|
19
|
-
|
19
|
+
}
|
20
20
|
end
|
21
21
|
|
22
22
|
def page_range(current_page, total_pages, window: 2)
|
@@ -10,7 +10,8 @@ module SolidQueueDashboard
|
|
10
10
|
"blue": "circle-blue",
|
11
11
|
"green": "circle-green",
|
12
12
|
"yellow": "circle-yellow",
|
13
|
-
"purple": "circle-purple"
|
13
|
+
"purple": "circle-purple",
|
14
|
+
"sky": "circle-sky",
|
14
15
|
}[Process::KIND_COLORS[kind]&.to_sym || :zinc]
|
15
16
|
end
|
16
17
|
|
@@ -24,7 +25,8 @@ module SolidQueueDashboard
|
|
24
25
|
"blue": "badge-blue",
|
25
26
|
"green": "badge-green",
|
26
27
|
"yellow": "badge-yellow",
|
27
|
-
"purple": "badge-purple"
|
28
|
+
"purple": "badge-purple",
|
29
|
+
"sky": "badge-sky",
|
28
30
|
}[Process::KIND_COLORS[kind]&.to_sym || :zinc]
|
29
31
|
end
|
30
32
|
|
@@ -8,14 +8,16 @@
|
|
8
8
|
|
9
9
|
<%= stylesheet_link_tag "solid_queue_dashboard/application", media: "all" %>
|
10
10
|
<%= javascript_include_tag "solid_queue_dashboard/application" %>
|
11
|
+
<%= javascript_include_tag "chartkick" %>
|
12
|
+
<%= javascript_include_tag "Chart.bundle" %>
|
11
13
|
<%= javascript_include_tag "solid_queue_dashboard/alpine", defer: true %>
|
12
14
|
</head>
|
13
15
|
<body class="pb-[15vh] sm:pb-[25vh]">
|
14
16
|
<div class="max-w-[1920px] mx-auto px-4 sm:px-6 lg:px-8">
|
15
|
-
<%= render
|
16
|
-
<%= render
|
17
|
+
<%= render "navbar" %>
|
18
|
+
<%= render "flash_messages" %>
|
17
19
|
<%= yield %>
|
18
|
-
<%= render
|
20
|
+
<%= render "footer" %>
|
19
21
|
</div>
|
20
22
|
</body>
|
21
23
|
</html>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<nav class="navbar mb-6">
|
2
|
-
<%= link_to root_path, class: "inline-flex items-center gap-0.5 text-xl font-bold tracking-tight translate-y-px" do %>
|
2
|
+
<%= link_to root_path, class: "inline-flex items-center gap-0.5 text-xl font-bold tracking-tight translate-y-px pl-1" do %>
|
3
3
|
<span class="circle circle-blue"></span>
|
4
4
|
<span class="circle circle-green"></span>
|
5
5
|
<span class="circle circle-yellow"></span>
|
@@ -21,18 +21,41 @@
|
|
21
21
|
<%= link_to processes_path, class: "navbar-item #{current_page?(processes_path) ? 'navbar-item-current' : 'navbar-item-default'}" do %>
|
22
22
|
<%= icon_server class: "size-4 mr-1.5" %>
|
23
23
|
Processes
|
24
|
+
|
25
|
+
<span class="badge <%= current_page?(processes_path) ? 'badge-secondary' : 'badge-primary' %> ml-1 text-xs">
|
26
|
+
<%= SolidQueue::Process.count %>
|
27
|
+
</span>
|
24
28
|
<% end %>
|
25
29
|
|
26
30
|
<%= link_to recurring_tasks_path, class: "navbar-item #{current_page?(recurring_tasks_path) ? 'navbar-item-current' : 'navbar-item-default'}" do %>
|
27
31
|
<%= icon_clock class: "size-4 mr-1.5" %>
|
28
32
|
Recurring Tasks
|
33
|
+
|
34
|
+
<span class="badge <%= current_page?(recurring_tasks_path) ? 'badge-secondary' : 'badge-primary' %> ml-1 text-xs">
|
35
|
+
<%= SolidQueue::RecurringTask.count %>
|
36
|
+
</span>
|
37
|
+
<% end %>
|
38
|
+
|
39
|
+
<%= link_to stats_path, class: "navbar-item #{current_page?(stats_path) ? 'navbar-item-current' : 'navbar-item-default'}" do %>
|
40
|
+
<%= icon_chart_scatter class: "size-4 mr-1.5" %>
|
41
|
+
Stats
|
29
42
|
<% end %>
|
30
43
|
</div>
|
31
44
|
|
32
|
-
<div class="ml-auto">
|
33
|
-
|
34
|
-
|
35
|
-
|
45
|
+
<div class="ml-auto flex gap-4">
|
46
|
+
<% if current_page?(controller: 'dashboard', action: 'index') %>
|
47
|
+
<%= form_with url: root_path, method: :get, class: "flex gap-1.5 items-center" do |form| %>
|
48
|
+
<%= form.hidden_field :chart_period %>
|
49
|
+
|
50
|
+
<%= icon_refresh_cw class: "size-4 text-muted-foreground" %>
|
51
|
+
<%= form.select :auto_refresh_period,
|
52
|
+
[["Auto-Refresh Off", "off"], ["15 seconds", "15"], ["30 seconds", "30"], ["1 minute", "60"], ["3 minutes", "180"], ["5 minutes", "300"], ["10 minutes", "600"], ["15 minutes", "900"], ["30 minutes", "1800"], ["1 hour", "3600"]],
|
53
|
+
{ selected: params[:auto_refresh_period].presence || "off" },
|
54
|
+
class: "select w-40",
|
55
|
+
data: { auto_submit: true }
|
56
|
+
%>
|
57
|
+
<% end %>
|
58
|
+
<% end %>
|
36
59
|
|
37
60
|
<%= form_with url: toggle_appearance_path, method: :post do |f| %>
|
38
61
|
<button
|
@@ -1,4 +1,18 @@
|
|
1
|
-
<div class="
|
1
|
+
<div class="card card-content p-6 relative">
|
2
|
+
<%= form_with url: root_path, method: :get, class: "absolute top-6 right-6" do |form| %>
|
3
|
+
<%= form.hidden_field :auto_refresh_period %>
|
4
|
+
<%= form.select :chart_period,
|
5
|
+
[["15 minutes", "15m"], ["30 minutes", "30m"], ["1 hour", "1h"], ["3 hours", "3h"], ["6 hours", "6h"], ["12 hours", "12h"], ["1 day", "1d"], ["3 days", "3d"], ["1 week", "1w"]],
|
6
|
+
{ selected: params[:chart_period].presence || "30m" },
|
7
|
+
class: "select",
|
8
|
+
data: { auto_submit: true }
|
9
|
+
%>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<%= line_chart @charts, points: false, thousands: "," %>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="grid grid-cols-6 gap-4 mt-4">
|
2
16
|
<% SolidQueueDashboard::Job::STATUSES.each do |status| %>
|
3
17
|
<div class="card" data-href="<%= jobs_path(status:) %>">
|
4
18
|
<div class="card-content pt-5">
|
@@ -7,35 +21,9 @@
|
|
7
21
|
<span class="ml-0.5 -translate-y-px circle <%= job_status_circle_class(status) %>"></span>
|
8
22
|
</h4>
|
9
23
|
<p class="text-4xl font-bold mt-1 text-black dark:text-white">
|
10
|
-
<%= @jobs.with_status(status).count %>
|
24
|
+
<%= number_with_delimiter(@jobs.with_status(status).count) %>
|
11
25
|
</p>
|
12
26
|
</div>
|
13
27
|
</div>
|
14
28
|
<% end %>
|
15
29
|
</div>
|
16
|
-
|
17
|
-
<div class="card mt-4">
|
18
|
-
<div class="card-header border-b">
|
19
|
-
<h3 class="card-title">Failure Rate</h3>
|
20
|
-
</div>
|
21
|
-
<div class="card-content !p-0">
|
22
|
-
<div class="table-wrapper">
|
23
|
-
<table class="table">
|
24
|
-
<thead class="table-header">
|
25
|
-
<tr class="table-row">
|
26
|
-
<th class="table-head">Job</th>
|
27
|
-
<th class="table-head">Failure Rate</th>
|
28
|
-
</tr>
|
29
|
-
</thead>
|
30
|
-
<tbody class="table-body">
|
31
|
-
<% @job_class_names.each do |class_name| %>
|
32
|
-
<tr class="table-row" data-href="<%= jobs_path(class_name:, status: :failed) %>">
|
33
|
-
<td class="table-cell font-medium"><%= class_name.titleize %></td>
|
34
|
-
<td class="table-cell font-medium"><%= format_failure_rate(SolidQueueDashboard.decorate(SolidQueue::Job.where(class_name:)).failure_rate) %></td>
|
35
|
-
</tr>
|
36
|
-
<% end %>
|
37
|
-
</tbody>
|
38
|
-
</table>
|
39
|
-
</div>
|
40
|
-
</div>
|
41
|
-
</div>
|
@@ -13,7 +13,7 @@
|
|
13
13
|
</tr>
|
14
14
|
</thead>
|
15
15
|
<tbody class="table-body">
|
16
|
-
<%= render partial:
|
16
|
+
<%= render partial: "solid_queue_dashboard/jobs/table_row", collection: jobs, as: :job, locals: { highlight_ids: highlight_ids } %>
|
17
17
|
</tbody>
|
18
18
|
</table>
|
19
19
|
</div>
|
@@ -14,6 +14,12 @@
|
|
14
14
|
|
15
15
|
<td class="table-cell">
|
16
16
|
<%= job_status_badge(job.status) %>
|
17
|
+
<% if job.running? %>
|
18
|
+
<br />
|
19
|
+
<span class="text-xs text-muted-foreground">
|
20
|
+
<%= time_ago_in_words(job.claimed_execution.created_at, include_seconds: true) %>
|
21
|
+
</span>
|
22
|
+
<% end %>
|
17
23
|
</td>
|
18
24
|
|
19
25
|
<td class="table-cell">
|
@@ -29,9 +35,9 @@
|
|
29
35
|
<p class="font-medium"><%= job.class_name %></p>
|
30
36
|
|
31
37
|
<% if job.arguments["arguments"].present? %>
|
32
|
-
<p class="mt-0.5">
|
38
|
+
<p class="inline-flex flex-wrap gap-0.5 items-start mt-0.5">
|
33
39
|
<% job.arguments["arguments"].each do |argument| %>
|
34
|
-
<span class="badge badge-zinc"><%= truncate(JSON.pretty_generate(argument), length:
|
40
|
+
<span class="badge badge-zinc text-xs"><%= truncate(JSON.pretty_generate(argument), length: 60) %></span>
|
35
41
|
<% end %>
|
36
42
|
</p>
|
37
43
|
<% end %>
|
@@ -51,7 +57,17 @@
|
|
51
57
|
</td>
|
52
58
|
|
53
59
|
<td class="table-cell">
|
54
|
-
<% if job.
|
60
|
+
<% if job.running? %>
|
61
|
+
<p class="text-sm text-muted-foreground">
|
62
|
+
Running for <strong class="font-medium text-foreground"><%= time_ago_in_words(job.claimed_execution.created_at, include_seconds: true) %></strong>
|
63
|
+
<% if job.claimed_execution.process %>
|
64
|
+
by <strong class="font-medium text-foreground">
|
65
|
+
<%= process_kind_badge(job.claimed_execution.process.kind) %>
|
66
|
+
<%= link_to "##{job.claimed_execution.process.id}", process_path(job.claimed_execution.process), class: "link" %>
|
67
|
+
</strong>
|
68
|
+
<% end %>
|
69
|
+
</p>
|
70
|
+
<% elsif job.success? || job.retried? %>
|
55
71
|
<p class="text-sm text-muted-foreground">
|
56
72
|
<%= job.retried? ? "Failed" : "Finished" %> at <strong class="font-medium text-foreground" data-date="<%= job.finished_at.to_fs(:database) %>"><%= job.finished_at %></strong><br />
|
57
73
|
<span class="text-xs"><%= time_ago_in_words(job.scheduled_at, include_seconds: true) %> ago</span>
|
@@ -25,12 +25,12 @@
|
|
25
25
|
<% end %>
|
26
26
|
</div>
|
27
27
|
|
28
|
-
<% if @pagination
|
28
|
+
<% if @pagination[:total_pages] > 1 %>
|
29
29
|
<div class="card-footer pt-6 border-t flex justify-between">
|
30
30
|
<%= render partial: 'solid_queue_dashboard/application/pagination', locals: {
|
31
|
-
current_page: @pagination
|
32
|
-
total_pages: @pagination
|
33
|
-
per_page: @pagination
|
31
|
+
current_page: @pagination[:current_page],
|
32
|
+
total_pages: @pagination[:total_pages],
|
33
|
+
per_page: @pagination[:per_page]
|
34
34
|
} %>
|
35
35
|
|
36
36
|
<div class="flex items-center gap-2">
|
@@ -42,7 +42,7 @@
|
|
42
42
|
<%= f.hidden_field :page, value: 1 %>
|
43
43
|
<%= f.select :per_page,
|
44
44
|
[10, 25, 50, 100],
|
45
|
-
{ selected: @pagination
|
45
|
+
{ selected: @pagination[:per_page] },
|
46
46
|
class: "select w-24",
|
47
47
|
data: { auto_submit: true }
|
48
48
|
%>
|
@@ -126,9 +126,11 @@
|
|
126
126
|
<% if @job.class_name === SolidQueueDashboard::Job::COMMAND_CLASS_NAME %>
|
127
127
|
<pre x-show="!showAll"><%= @job.arguments["arguments"][0] %></pre>
|
128
128
|
<% else %>
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
<div x-show="!showAll">
|
130
|
+
<% @job.arguments["arguments"].each do |argument| %>
|
131
|
+
<span class="badge badge-zinc text-sm"><%= JSON.pretty_generate(argument) %></span>
|
132
|
+
<% end %>
|
133
|
+
</div>
|
132
134
|
<% end %>
|
133
135
|
|
134
136
|
<pre x-show="showAll"><%= JSON.pretty_generate(@job.arguments) %></pre>
|
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
<td class="table-cell">
|
7
7
|
<%= process_kind_badge(process.kind) %>
|
8
|
+
|
9
|
+
<% if process.claimed_executions.any? %>
|
10
|
+
<span class="ml-0.5 text-xs text-muted-foreground">
|
11
|
+
Running <%= pluralize(process.claimed_executions.count, "job") %>
|
12
|
+
</span>
|
13
|
+
<% end %>
|
8
14
|
</td>
|
9
15
|
|
10
16
|
<td class="table-cell"><%= process.hostname %></td>
|
@@ -66,9 +66,22 @@
|
|
66
66
|
</div>
|
67
67
|
</div>
|
68
68
|
|
69
|
+
<% if @process.claimed_executions.any? %>
|
70
|
+
<div class="card mt-8 overflow-hidden">
|
71
|
+
<div class="card-header border-b border-t-4 border-t-sky-500 dark:border-t-sky-600">
|
72
|
+
<h2 class="card-title">Running Jobs</h2>
|
73
|
+
<p class="card-description">Jobs that this process is currently running</p>
|
74
|
+
</div>
|
75
|
+
|
76
|
+
<div class="card-content p-0">
|
77
|
+
<%= render partial: 'solid_queue_dashboard/jobs/table', locals: { jobs: @process.claimed_executions.map { |execution| SolidQueueDashboard::Decorators::JobDecorator.new(execution.job) } } %>
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
<% end %>
|
81
|
+
|
69
82
|
<% if @process.metadata.present? %>
|
70
83
|
<div class="card mt-8 overflow-hidden">
|
71
|
-
<div class="card-header border-b border-t-4 border-t-
|
84
|
+
<div class="card-header border-b border-t-4 border-t-amber-500 dark:border-t-amber-600">
|
72
85
|
<h2 class="card-title">Metadata</h2>
|
73
86
|
<p class="card-description">Additional information about this process</p>
|
74
87
|
</div>
|
@@ -122,7 +122,12 @@
|
|
122
122
|
<div class="card-content pt-5">
|
123
123
|
<ul class="list-decimal marker:text-sm marker:text-muted-foreground ml-4 space-y-1">
|
124
124
|
<% @recurring_task.next_runs.each do |run_time| %>
|
125
|
-
<li
|
125
|
+
<li>
|
126
|
+
<span data-date><%= run_time.strftime("%Y-%m-%d %H:%M:%S %Z") %></span>
|
127
|
+
<span class="text-muted-foreground">
|
128
|
+
in <%= distance_of_time_in_words(Time.current, run_time) %>
|
129
|
+
</span>
|
130
|
+
</li>
|
126
131
|
<% end %>
|
127
132
|
</ul>
|
128
133
|
</div>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<div class="card mt-4">
|
2
|
+
<div class="card-header border-b">
|
3
|
+
<h3 class="card-title">Failure Rate</h3>
|
4
|
+
</div>
|
5
|
+
<div class="card-content !p-0">
|
6
|
+
<div class="table-wrapper">
|
7
|
+
<table class="table">
|
8
|
+
<thead class="table-header">
|
9
|
+
<tr class="table-row">
|
10
|
+
<th class="table-head">Job</th>
|
11
|
+
<th class="table-head">Failure Rate</th>
|
12
|
+
</tr>
|
13
|
+
</thead>
|
14
|
+
<tbody class="table-body">
|
15
|
+
<% @job_class_names.each do |class_name| %>
|
16
|
+
<tr class="table-row" data-href="<%= jobs_path(class_name:, status: :failed) %>">
|
17
|
+
<td class="table-cell font-medium"><%= class_name.titleize %></td>
|
18
|
+
<td class="table-cell font-medium"><%= format_failure_rate(SolidQueueDashboard.decorate(SolidQueue::Job.where(class_name:)).failure_rate) %></td>
|
19
|
+
</tr>
|
20
|
+
<% end %>
|
21
|
+
</tbody>
|
22
|
+
</table>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
</div>
|
data/config/routes.rb
CHANGED
@@ -8,7 +8,9 @@ module SolidQueueDashboard
|
|
8
8
|
def status
|
9
9
|
return @status if defined?(@status)
|
10
10
|
|
11
|
-
@status = if
|
11
|
+
@status = if running?
|
12
|
+
Job::RUNNING
|
13
|
+
elsif retried?
|
12
14
|
Job::RETRIED
|
13
15
|
elsif failed?
|
14
16
|
Job::FAILED
|
@@ -21,6 +23,11 @@ module SolidQueueDashboard
|
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
26
|
+
def running?
|
27
|
+
return @running if defined?(@running)
|
28
|
+
@running = claimed_execution.present?
|
29
|
+
end
|
30
|
+
|
24
31
|
def success?
|
25
32
|
return @success if defined?(@success)
|
26
33
|
@success = finished_at.present? && !failed? && !retried?
|
@@ -44,7 +51,7 @@ module SolidQueueDashboard
|
|
44
51
|
|
45
52
|
def pending?
|
46
53
|
return @pending if defined?(@pending)
|
47
|
-
@pending = !finished_at.present?
|
54
|
+
@pending = !finished_at.present? && !running?
|
48
55
|
end
|
49
56
|
|
50
57
|
def execution_history
|
@@ -3,6 +3,8 @@ module SolidQueueDashboard
|
|
3
3
|
class JobsDecorator < SimpleDelegator
|
4
4
|
def with_status(status)
|
5
5
|
case status.to_sym
|
6
|
+
when Job::RUNNING
|
7
|
+
running
|
6
8
|
when Job::SUCCESS
|
7
9
|
success
|
8
10
|
when Job::FAILED
|
@@ -18,6 +20,10 @@ module SolidQueueDashboard
|
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
23
|
+
def running
|
24
|
+
where.associated(:claimed_execution)
|
25
|
+
end
|
26
|
+
|
21
27
|
def success
|
22
28
|
where.not(finished_at: nil)
|
23
29
|
.where.not(id: failed)
|
@@ -31,6 +37,7 @@ module SolidQueueDashboard
|
|
31
37
|
def pending
|
32
38
|
where(finished_at: nil, scheduled_at: ..Time.current)
|
33
39
|
.where.not(id: failed)
|
40
|
+
.where.not(id: running)
|
34
41
|
end
|
35
42
|
|
36
43
|
def retried
|
@@ -1,20 +1,22 @@
|
|
1
1
|
module SolidQueueDashboard
|
2
2
|
module Job
|
3
3
|
# Constants
|
4
|
+
RUNNING = :running
|
4
5
|
SUCCESS = :success
|
5
6
|
RETRIED = :retried
|
6
7
|
FAILED = :failed
|
7
8
|
PENDING = :pending
|
8
9
|
SCHEDULED = :scheduled
|
9
10
|
|
10
|
-
STATUSES = [ SUCCESS, RETRIED, FAILED, SCHEDULED, PENDING ]
|
11
|
+
STATUSES = [ RUNNING, SUCCESS, RETRIED, FAILED, SCHEDULED, PENDING ]
|
11
12
|
|
12
13
|
STATUS_COLORS = {
|
13
14
|
SUCCESS => "green",
|
14
15
|
RETRIED => "amber",
|
15
16
|
FAILED => "red",
|
16
|
-
SCHEDULED => "
|
17
|
-
PENDING => "zinc"
|
17
|
+
SCHEDULED => "purple",
|
18
|
+
PENDING => "zinc",
|
19
|
+
RUNNING => "sky"
|
18
20
|
}
|
19
21
|
|
20
22
|
COMMAND_CLASS_NAME = "SolidQueue::RecurringJob"
|
@@ -9,13 +9,13 @@ module SolidQueueDashboard
|
|
9
9
|
KINDS = [ SUPERVISOR, DISPATCHER, WORKER, SCHEDULER ]
|
10
10
|
|
11
11
|
KIND_COLORS = {
|
12
|
-
SUPERVISOR => "
|
12
|
+
SUPERVISOR => "yellow",
|
13
13
|
DISPATCHER => "green",
|
14
|
-
WORKER => "
|
14
|
+
WORKER => "sky",
|
15
15
|
SCHEDULER => "purple"
|
16
16
|
}
|
17
17
|
|
18
|
-
HEARTBEAT_DEAD_THRESHOLD =
|
18
|
+
HEARTBEAT_DEAD_THRESHOLD = 3.minutes
|
19
19
|
|
20
20
|
def self.kind_color(kind)
|
21
21
|
KIND_COLORS[kind] || "zinc"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_queue_dashboard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kodkod
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: solid_queue
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: groupdate
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '6.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '6.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: chartkick
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
27
55
|
description: Dashboard for Solid Queue
|
28
56
|
email:
|
29
57
|
- andrew@kodkod.me
|
@@ -48,6 +76,7 @@ files:
|
|
48
76
|
- app/controllers/solid_queue_dashboard/jobs_controller.rb
|
49
77
|
- app/controllers/solid_queue_dashboard/processes_controller.rb
|
50
78
|
- app/controllers/solid_queue_dashboard/recurring_tasks_controller.rb
|
79
|
+
- app/controllers/solid_queue_dashboard/stats_controller.rb
|
51
80
|
- app/helpers/solid_queue_dashboard/appearance_helper.rb
|
52
81
|
- app/helpers/solid_queue_dashboard/application_helper.rb
|
53
82
|
- app/helpers/solid_queue_dashboard/icons_helper.rb
|
@@ -76,6 +105,7 @@ files:
|
|
76
105
|
- app/views/solid_queue_dashboard/recurring_tasks/_table_row.html.erb
|
77
106
|
- app/views/solid_queue_dashboard/recurring_tasks/index.html.erb
|
78
107
|
- app/views/solid_queue_dashboard/recurring_tasks/show.html.erb
|
108
|
+
- app/views/solid_queue_dashboard/stats/index.html.erb
|
79
109
|
- bun.lockb
|
80
110
|
- config/routes.rb
|
81
111
|
- lib/solid_queue_dashboard.rb
|