rails_live_dashboard 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -0
- data/app/assets/builds/rails_live_dashboard/application.css +1 -1
- data/app/components/rails_live_dashboard/job_status_badge_component.html.erb +3 -0
- data/app/components/rails_live_dashboard/job_status_badge_component.rb +21 -0
- data/app/controllers/rails_live_dashboard/jobs_controller.rb +12 -0
- data/app/helpers/rails_live_dashboard/jobs_helper.rb +29 -0
- data/app/models/rails_live_dashboard/current.rb +10 -0
- data/app/models/rails_live_dashboard/job.rb +15 -0
- data/app/models/rails_live_dashboard/types/job_content.rb +49 -0
- data/app/views/rails_live_dashboard/jobs/_queries.html.erb +11 -0
- data/app/views/rails_live_dashboard/jobs/_relateds.html.erb +16 -0
- data/app/views/rails_live_dashboard/jobs/_tabs.html.erb +14 -0
- data/app/views/rails_live_dashboard/jobs/index.html.erb +54 -0
- data/app/views/rails_live_dashboard/jobs/show.html.erb +65 -0
- data/app/views/rails_live_dashboard/shared/_header.html.erb +2 -0
- data/config/routes.rb +1 -0
- data/lib/rails_live_dashboard/engine.rb +4 -0
- data/lib/rails_live_dashboard/exceptions/entry_not_found.rb +9 -0
- data/lib/rails_live_dashboard/recorders/jobs/discard_recorder.rb +46 -0
- data/lib/rails_live_dashboard/recorders/jobs/enqueue_recorder.rb +40 -0
- data/lib/rails_live_dashboard/recorders/jobs/perform_recorder.rb +46 -0
- data/lib/rails_live_dashboard/recorders/jobs/start_recorder.rb +41 -0
- data/lib/rails_live_dashboard/subscribers/active_job_subscriber.rb +52 -0
- data/lib/rails_live_dashboard/version.rb +1 -1
- data/lib/rails_live_dashboard.rb +6 -0
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69a7b82a9771de7bdd92f6533e90ccd756df95f0bcc90d596ac67e4e1c7293f1
|
4
|
+
data.tar.gz: 3a0c008151f505f0b0a78519e7d180405e66b3ddcd9c0a4f2655ad4b9de049ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e81b72444d9fda7991414e07f48ce2b0fdce26ba7275dc37d78e18737a8d55190a1084b7749283814e1906443322c318e86e29f0ca9141d57fbcf7f47841818
|
7
|
+
data.tar.gz: a36fafe7403b6e7bc5aae9da1f89e4a3843b12eab024e956764d70628d5b047bb0f70c1ec5c4038c82665226f93bf964a9ef7bab0e3e97145b3d9838bb4d14b9
|
data/README.md
CHANGED
@@ -4,6 +4,18 @@
|
|
4
4
|
|
5
5
|
RailsLiveDashboard is a real-time debug dashboard for Rails applications. It provides insights into requests, queries, and exceptions, empowering developers to debug their applications effectively in development environments.
|
6
6
|
|
7
|
+
## Modules Implemented
|
8
|
+
|
9
|
+
- Dashboard: Show environment ruby and rails version, slowest requests and slowest queries.
|
10
|
+
|
11
|
+
- Requests: Show requests data, like controller, action, status code, params and duration.
|
12
|
+
|
13
|
+
- Exceptions: Show exception data, like traces and file.
|
14
|
+
|
15
|
+
- Queries: Show queries data, like SQL and duration.
|
16
|
+
|
17
|
+
- Jobs: Show jobs data, like name, job_id, paramers, history, status and duration.
|
18
|
+
|
7
19
|
**Note: This gem is not recommended for production use, as it may potentially slow down your application and significantly increase your database size.**
|
8
20
|
|
9
21
|
![Dashboard screen](docs/dashboard.png "Dashboard screen")
|
@@ -1 +1 @@
|
|
1
|
-
/*! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.relative{position:relative}.isolate{isolation:isolate}.z-10{z-index:10}.-mx-4{margin-left:-1rem;margin-right:-1rem}.-my-2{margin-top:-.5rem;margin-bottom:-.5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.flow-root{display:flow-root}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-16{height:4rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-16{width:4rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-full{width:100%}.min-w-0{min-width:0}.min-w-full{min-width:100%}.max-w-7xl{max-width:80rem}.flex-auto{flex:1 1 auto}.flex-none{flex:none}.flex-shrink-0,.shrink-0{flex-shrink:0}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-10{gap:2.5rem}.gap-5{gap:1.25rem}.gap-x-1{-moz-column-gap:.25rem;column-gap:.25rem}.gap-x-1\.5{-moz-column-gap:.375rem;column-gap:.375rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.divide-gray-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(209 213 219/var(--tw-divide-opacity))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border-b-2{border-bottom-width:2px}.border-t{border-top-width:1px}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity))}.bg-orange-50{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-orange-700{--tw-gradient-from:#c2410c var(--tw-gradient-from-position);--tw-gradient-to:#c2410c00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-700{--tw-gradient-from:#b91c1c var(--tw-gradient-from-position);--tw-gradient-to:#b91c1c00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-orange-500{--tw-gradient-to:#f97316 var(--tw-gradient-to-position)}.to-red-500{--tw-gradient-to:#ef4444 var(--tw-gradient-to-position)}.fill-blue-500{fill:#3b82f6}.fill-green-500{fill:#22c55e}.fill-indigo-500{fill:#6366f1}.fill-purple-500{fill:#a855f7}.fill-red-500{fill:#ef4444}.fill-yellow-500{fill:#eab308}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-10{padding-bottom:2.5rem}.pb-3{padding-bottom:.75rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-7{line-height:1.75rem}.tracking-tight{letter-spacing:-.025em}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity))}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-blue-700\/10{--tw-ring-color:#1d4ed81a}.ring-gray-200{--tw-ring-opacity:1;--tw-ring-color:rgb(229 231 235/var(--tw-ring-opacity))}.ring-gray-900\/5{--tw-ring-color:#1118270d}.ring-green-600\/20{--tw-ring-color:#16a34a33}.ring-orange-600\/10{--tw-ring-color:#ea580c1a}.ring-red-600\/10{--tw-ring-color:#dc26261a}.ring-yellow-600\/20{--tw-ring-color:#ca8a0433}.ring-opacity-5{--tw-ring-opacity:0.05}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.hover\:text-indigo-900:hover{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-inset:focus{--tw-ring-inset:inset}.focus\:ring-white:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}@media (min-width:640px){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:mt-0{margin-top:0}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-col{flex-direction:column}.sm\:items-end{align-items:flex-end}.sm\:gap-4{gap:1rem}.sm\:rounded-lg{border-radius:.5rem}.sm\:p-6{padding:1.5rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-6{padding-right:1.5rem}.sm\:pl-6,.sm\:px-6{padding-left:1.5rem}.sm\:pr-6{padding-right:1.5rem}}@media (min-width:1024px){.lg\:relative{position:relative}.lg\:z-10{z-index:10}.lg\:-mx-8{margin-left:-2rem;margin-right:-2rem}.lg\:ml-4{margin-left:1rem}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:items-center{align-items:center}.lg\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem*var(--tw-space-x-reverse));margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)))}.lg\:divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.lg\:divide-gray-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 65 81/var(--tw-divide-opacity))}.lg\:px-0{padding-left:0;padding-right:0}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-2{padding-top:.5rem;padding-bottom:.5rem}}
|
1
|
+
/*! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.absolute{position:absolute}.relative{position:relative}.left-4{left:1rem}.top-4{top:1rem}.isolate{isolation:isolate}.z-10{z-index:10}.-mx-4{margin-left:-1rem;margin-right:-1rem}.-my-2{margin-top:-.5rem;margin-bottom:-.5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-2{margin-top:.5rem;margin-bottom:.5rem}.-mb-8{margin-bottom:-2rem}.-ml-px{margin-left:-1px}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.flow-root{display:flow-root}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-16{height:4rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-full{height:100%}.w-0{width:0}.w-0\.5{width:.125rem}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-16{width:4rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.min-w-0{min-width:0}.min-w-full{min-width:100%}.max-w-7xl{max-width:80rem}.flex-1{flex:1 1 0%}.flex-auto{flex:1 1 auto}.flex-none{flex:none}.flex-shrink-0,.shrink-0{flex-shrink:0}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-10{gap:2.5rem}.gap-5{gap:1.25rem}.gap-8{gap:2rem}.gap-x-1{-moz-column-gap:.25rem;column-gap:.25rem}.gap-x-1\.5{-moz-column-gap:.375rem;column-gap:.375rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.divide-gray-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(209 213 219/var(--tw-divide-opacity))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border-b-2{border-bottom-width:2px}.border-t{border-top-width:1px}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity))}.bg-orange-50{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-orange-700{--tw-gradient-from:#c2410c var(--tw-gradient-from-position);--tw-gradient-to:#c2410c00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-700{--tw-gradient-from:#b91c1c var(--tw-gradient-from-position);--tw-gradient-to:#b91c1c00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-orange-500{--tw-gradient-to:#f97316 var(--tw-gradient-to-position)}.to-red-500{--tw-gradient-to:#ef4444 var(--tw-gradient-to-position)}.fill-blue-500{fill:#3b82f6}.fill-green-500{fill:#22c55e}.fill-indigo-500{fill:#6366f1}.fill-purple-500{fill:#a855f7}.fill-red-500{fill:#ef4444}.fill-yellow-500{fill:#eab308}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-10{padding-bottom:2.5rem}.pb-3{padding-bottom:.75rem}.pb-8{padding-bottom:2rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-1{padding-top:.25rem}.pt-1\.5{padding-top:.375rem}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-7{line-height:1.75rem}.tracking-tight{letter-spacing:-.025em}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity))}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-blue-700\/10{--tw-ring-color:#1d4ed81a}.ring-gray-200{--tw-ring-opacity:1;--tw-ring-color:rgb(229 231 235/var(--tw-ring-opacity))}.ring-gray-900\/5{--tw-ring-color:#1118270d}.ring-green-600\/20{--tw-ring-color:#16a34a33}.ring-green-700\/10{--tw-ring-color:#15803d1a}.ring-orange-600\/10{--tw-ring-color:#ea580c1a}.ring-orange-700\/10{--tw-ring-color:#c2410c1a}.ring-red-600\/10{--tw-ring-color:#dc26261a}.ring-red-600\/20{--tw-ring-color:#dc262633}.ring-red-700\/10{--tw-ring-color:#b91c1c1a}.ring-yellow-600\/20{--tw-ring-color:#ca8a0433}.ring-opacity-5{--tw-ring-opacity:0.05}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.hover\:text-indigo-900:hover{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-inset:focus{--tw-ring-inset:inset}.focus\:ring-white:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}@media (min-width:640px){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:mt-0{margin-top:0}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-col{flex-direction:column}.sm\:items-end{align-items:flex-end}.sm\:gap-4{gap:1rem}.sm\:rounded-lg{border-radius:.5rem}.sm\:p-6{padding:1.5rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-6{padding-right:1.5rem}.sm\:pl-6,.sm\:px-6{padding-left:1.5rem}.sm\:pr-6{padding-right:1.5rem}}@media (min-width:768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:relative{position:relative}.lg\:z-10{z-index:10}.lg\:-mx-8{margin-left:-2rem;margin-right:-2rem}.lg\:ml-4{margin-left:1rem}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:items-center{align-items:center}.lg\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem*var(--tw-space-x-reverse));margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)))}.lg\:divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.lg\:divide-gray-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 65 81/var(--tw-divide-opacity))}.lg\:px-0{padding-left:0;padding-right:0}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-2{padding-top:.5rem;padding-bottom:.5rem}}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsLiveDashboard
|
4
|
+
class JobStatusBadgeComponent < ViewComponent::Base
|
5
|
+
CLASSES = {
|
6
|
+
enqueued: 'bg-orange-50 text-orange-700 ring-orange-700/10',
|
7
|
+
started: 'bg-blue-50 text-blue-700 ring-blue-700/10',
|
8
|
+
performed: 'bg-green-50 text-green-700 ring-green-600/20',
|
9
|
+
discarted: 'bg-red-50 text-red-800 ring-red-600/20'
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def initialize(status)
|
13
|
+
super
|
14
|
+
|
15
|
+
return if status.nil?
|
16
|
+
|
17
|
+
@status = status.upcase
|
18
|
+
@classes = CLASSES[status.downcase.to_sym]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RailsLiveDashboard
|
2
|
+
module JobsHelper
|
3
|
+
def job_history_icon(status)
|
4
|
+
case status
|
5
|
+
when 'enqueued'
|
6
|
+
lucide_icon('list-start',
|
7
|
+
class: 'h-8 w-8 p-1.5 rounded-full flex items-center justify-center ring-1 ring-inset bg-orange-50 text-orange-700 ring-orange-700/10')
|
8
|
+
when 'started'
|
9
|
+
lucide_icon('list-video',
|
10
|
+
class: 'h-8 w-8 p-1.5 rounded-full flex items-center justify-center ring-1 ring-inset bg-blue-50 text-blue-700 ring-blue-700/10')
|
11
|
+
when 'performed'
|
12
|
+
lucide_icon('check',
|
13
|
+
class: 'h-8 w-8 p-1.5 rounded-full flex items-center justify-center ring-1 ring-inset bg-green-50 text-green-700 ring-green-700/10')
|
14
|
+
when 'discarted'
|
15
|
+
lucide_icon('trash',
|
16
|
+
class: 'h-8 w-8 p-1.5 rounded-full flex items-center justify-center ring-1 ring-inset bg-red-50 text-red-700 ring-red-700/10')
|
17
|
+
else
|
18
|
+
lucide_icon('refresh-cw',
|
19
|
+
class: 'h-8 w-8 p-1.5 rounded-full flex items-center justify-center ring-1 ring-inset bg-orange-50 text-orange-700 ring-orange-700/10')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def job_duration_text(duration)
|
24
|
+
return "#{duration} ms" if duration < 1000
|
25
|
+
|
26
|
+
"#{(duration / 1000).round} s"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RailsLiveDashboard
|
2
|
+
class Job < Entry
|
3
|
+
attribute :content, Types::JobContent.new
|
4
|
+
|
5
|
+
scope :same, ->(job_id) { where("content->>'job_id' = ?", job_id) }
|
6
|
+
|
7
|
+
def performed?
|
8
|
+
content.status == 'performed'
|
9
|
+
end
|
10
|
+
|
11
|
+
def discarted?
|
12
|
+
content.status == 'discarted'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module RailsLiveDashboard
|
2
|
+
module Types
|
3
|
+
class JobContent < ActiveRecord::Type::Value
|
4
|
+
PAYLOAD =
|
5
|
+
%i[
|
6
|
+
job_name
|
7
|
+
job_id
|
8
|
+
params
|
9
|
+
status
|
10
|
+
queue_name
|
11
|
+
started_at
|
12
|
+
finished_at
|
13
|
+
duration
|
14
|
+
db_duration
|
15
|
+
error
|
16
|
+
history
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
CONTENT_STRUCT = Struct.new(*PAYLOAD)
|
20
|
+
|
21
|
+
def type
|
22
|
+
:jsonb
|
23
|
+
end
|
24
|
+
|
25
|
+
def cast(value)
|
26
|
+
return value if value.is_a?(CONTENT_STRUCT)
|
27
|
+
|
28
|
+
sanitized = sanitize_input(value)
|
29
|
+
CONTENT_STRUCT.new(**sanitized)
|
30
|
+
end
|
31
|
+
|
32
|
+
def deserialize(value)
|
33
|
+
decoded = ActiveSupport::JSON.decode(value)&.symbolize_keys
|
34
|
+
sanitized = sanitize_input(decoded)
|
35
|
+
CONTENT_STRUCT.new(**sanitized)
|
36
|
+
end
|
37
|
+
|
38
|
+
def serialize(value)
|
39
|
+
ActiveSupport::JSON.encode(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def sanitize_input(input)
|
45
|
+
input.slice(*PAYLOAD)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% if @queries.empty? %>
|
2
|
+
<p class="py-10 text-center">No queries for this job</p>
|
3
|
+
<% else %>
|
4
|
+
<div class="flow-root">
|
5
|
+
<div class="overflow-x-auto">
|
6
|
+
<div class="inline-block min-w-full align-middle">
|
7
|
+
<%= render 'rails_live_dashboard/queries/list' %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<div
|
2
|
+
class="overflow-hidden bg-white shadow sm:rounded-lg"
|
3
|
+
data-controller="tabs"
|
4
|
+
data-tabs-active-class="border-indigo-500 text-indigo-600"
|
5
|
+
data-tabs-default-tab-value="tab_queries"
|
6
|
+
>
|
7
|
+
<nav class="isolate flex rounded-t-lg shadow" aria-label="Tabs">
|
8
|
+
<button id="tab_queries" class="w-full text-gray-500 hover:text-gray-700 border-b-2 py-4 px-1 text-center text-sm font-medium" data-tabs-target="button" data-action="click->tabs#select">
|
9
|
+
Queries (<%= @queries.size %>)
|
10
|
+
</button>
|
11
|
+
</nav>
|
12
|
+
|
13
|
+
<div data-tabs-target="tab" id="tab_queries">
|
14
|
+
<%= render 'queries' %>
|
15
|
+
</div>
|
16
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div
|
2
|
+
class="overflow-hidden bg-white shadow sm:rounded-lg"
|
3
|
+
data-controller="tabs"
|
4
|
+
data-tabs-active-class="border-indigo-500 text-indigo-600"
|
5
|
+
data-tabs-default-tab-value="tab_params"
|
6
|
+
>
|
7
|
+
<nav class="isolate flex rounded-t-lg shadow" aria-label="Tabs">
|
8
|
+
<button id="tab_params" class="w-full text-gray-500 hover:text-gray-700 border-b-2 py-4 px-1 text-center text-sm font-medium" data-tabs-target="button" data-action="click->tabs#select">Params</button>
|
9
|
+
</nav>
|
10
|
+
|
11
|
+
<div class="p-5 bg-gray-800 text-white">
|
12
|
+
<pre data-tabs-target="tab" id="tab_params"><%= JSON.pretty_generate(@job.content.params) %></pre>
|
13
|
+
</div>
|
14
|
+
</div>
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<div class="mt-10">
|
2
|
+
<div class="mt-8 flow-root">
|
3
|
+
<div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
4
|
+
<div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
|
5
|
+
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg">
|
6
|
+
<table class="min-w-full divide-y divide-gray-300">
|
7
|
+
<thead class="bg-gray-50">
|
8
|
+
<tr>
|
9
|
+
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Job</th>
|
10
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Status</th>
|
11
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Date</th>
|
12
|
+
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
|
13
|
+
<span class="sr-only">Show</span>
|
14
|
+
</th>
|
15
|
+
</tr>
|
16
|
+
</thead>
|
17
|
+
<tbody class="divide-y divide-gray-200 bg-white">
|
18
|
+
<% if @jobs.empty? %>
|
19
|
+
<tr>
|
20
|
+
<td colspan="4">
|
21
|
+
<p class="p-5 text-center">No job registered</p>
|
22
|
+
</td>
|
23
|
+
</tr>
|
24
|
+
<% end %>
|
25
|
+
<% @jobs.each do |job| %>
|
26
|
+
<tr>
|
27
|
+
<td class="whitespace-nowrap py-4 pl-4 pr-3 sm:pl-6">
|
28
|
+
<div class="min-w-0 flex-auto">
|
29
|
+
<p class="text-base font-semibold text-gray-900">
|
30
|
+
<%= job.content.job_name %>
|
31
|
+
</p>
|
32
|
+
<p class="mt-1 text-sm text-gray-500">
|
33
|
+
Job ID: <%= job.content.job_id %>
|
34
|
+
</p>
|
35
|
+
</div>
|
36
|
+
</td>
|
37
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
38
|
+
<%= render RailsLiveDashboard::JobStatusBadgeComponent.new(job.content.status) %>
|
39
|
+
</td>
|
40
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
41
|
+
<%= job.created_at.strftime("%Y-%m-%d %H:%M:%S") %>
|
42
|
+
</td>
|
43
|
+
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
|
44
|
+
<%= link_to 'Show', rails_live_dashboard.job_path(job), class: 'text-indigo-600 hover:text-indigo-900' %>
|
45
|
+
</td>
|
46
|
+
</tr>
|
47
|
+
<% end %>
|
48
|
+
</tbody>
|
49
|
+
</table>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
</div>
|
@@ -0,0 +1,65 @@
|
|
1
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mt-10">
|
2
|
+
<div class="divide-y divide-gray-200 overflow-hidden rounded-lg bg-white shadow">
|
3
|
+
<div class="px-4 py-5 sm:px-6">
|
4
|
+
<h3 class="text-lg font-semibold text-gray-900"><%= @job.content.job_name %></h3>
|
5
|
+
<p class="mt-1 text-gray-500">Job ID: <%= @job.content.job_id %></p>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<dl class="divide-y divide-gray-100">
|
9
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
10
|
+
<dt class="text-sm font-medium text-gray-900">Date</dt>
|
11
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @job.created_at.strftime("%Y-%m-%d %H:%M:%S") %></dd>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<% if @job.performed? || @job.discarted? %>
|
15
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
16
|
+
<dt class="text-sm font-medium text-gray-900">Duration</dt>
|
17
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= job_duration_text(@job.content.duration) %></dd>
|
18
|
+
</div>
|
19
|
+
<% end %>
|
20
|
+
</dl>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="divide-y divide-gray-200 overflow-hidden rounded-lg bg-white shadow">
|
24
|
+
<div class="px-4 py-5 sm:px-6">
|
25
|
+
<p class="font-semibold">Job History</p>
|
26
|
+
</div>
|
27
|
+
<div class="px-4 py-5 sm:p-6">
|
28
|
+
<div class="flow-root">
|
29
|
+
<ul role="list" class="-mb-8">
|
30
|
+
<% @job.content.history.each do |history| %>
|
31
|
+
<li>
|
32
|
+
<div class="relative pb-8">
|
33
|
+
<% unless history == @job.content.history.last %>
|
34
|
+
<span class="absolute left-4 top-4 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true"></span>
|
35
|
+
<% end %>
|
36
|
+
|
37
|
+
<div class="relative flex space-x-3">
|
38
|
+
<div>
|
39
|
+
<%= job_history_icon(history["status"]) %>
|
40
|
+
</div>
|
41
|
+
<div class="flex min-w-0 flex-1 justify-between space-x-4 pt-1.5">
|
42
|
+
<div>
|
43
|
+
<p class="text-sm text-gray-500">Changed status to <b class="text-black"><%= history["status"] %></b></p>
|
44
|
+
</div>
|
45
|
+
<div class="whitespace-nowrap text-right text-sm text-gray-500">
|
46
|
+
<time><%= DateTime.parse(history["date"]).strftime("%Y-%m-%d %H:%M:%S") %></time>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
</li>
|
52
|
+
<% end %>
|
53
|
+
</ul>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
|
59
|
+
<div class="mt-10">
|
60
|
+
<%= render 'tabs' %>
|
61
|
+
</div>
|
62
|
+
|
63
|
+
<div class="mt-10">
|
64
|
+
<%= render 'relateds' %>
|
65
|
+
</div>
|
@@ -25,6 +25,7 @@
|
|
25
25
|
<%= link_to 'Requests', rails_live_dashboard.requests_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white inline-flex items-center rounded-md py-2 px-3 text-sm font-medium', 'bg-gray-900 text-white': current_page?(requests_path)) %>
|
26
26
|
<%= link_to 'Exceptions', rails_live_dashboard.exceptions_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white inline-flex items-center rounded-md py-2 px-3 text-sm font-medium', 'bg-gray-900 text-white': current_page?(exceptions_path)) %>
|
27
27
|
<%= link_to 'Queries', rails_live_dashboard.queries_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white inline-flex items-center rounded-md py-2 px-3 text-sm font-medium', 'bg-gray-900 text-white': current_page?(queries_path)) %>
|
28
|
+
<%= link_to 'Jobs', rails_live_dashboard.jobs_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white inline-flex items-center rounded-md py-2 px-3 text-sm font-medium', 'bg-gray-900 text-white': current_page?(jobs_path)) %>
|
28
29
|
</nav>
|
29
30
|
</div>
|
30
31
|
|
@@ -38,6 +39,7 @@
|
|
38
39
|
<%= link_to 'Requests', rails_live_dashboard.requests_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md py-2 px-3 text-base font-medium', 'bg-gray-900 text-white': current_page?(requests_path)) %>
|
39
40
|
<%= link_to 'Exceptions', rails_live_dashboard.exceptions_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md py-2 px-3 text-base font-medium', 'bg-gray-900 text-white': current_page?(exceptions_path)) %>
|
40
41
|
<%= link_to 'Queries', rails_live_dashboard.queries_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md py-2 px-3 text-base font-medium', 'bg-gray-900 text-white': current_page?(queries_path)) %>
|
42
|
+
<%= link_to 'Jobs', rails_live_dashboard.jobs_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md py-2 px-3 text-base font-medium', 'bg-gray-900 text-white': current_page?(jobs_path)) %>
|
41
43
|
</div>
|
42
44
|
</nav>
|
43
45
|
</header>
|
data/config/routes.rb
CHANGED
@@ -6,6 +6,7 @@ RailsLiveDashboard::Engine.routes.draw do
|
|
6
6
|
resources :requests, only: %i[index show]
|
7
7
|
resources :exceptions, only: %i[index show]
|
8
8
|
resources :queries, only: %i[index show]
|
9
|
+
resources :jobs, only: %i[index show]
|
9
10
|
|
10
11
|
namespace :widgets do
|
11
12
|
get 'slowest-requests', action: :show, controller: :slowest_requests
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module RailsLiveDashboard
|
2
|
+
module Recorders
|
3
|
+
module Jobs
|
4
|
+
class DiscardRecorder
|
5
|
+
def initialize(event)
|
6
|
+
@event = event
|
7
|
+
@job = Job.same(@event.payload[:job].job_id).first
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
raise Exceptions::EntryNotFound.new(:job, @event.payload[:job].job_id) if @job.nil?
|
12
|
+
|
13
|
+
build_content
|
14
|
+
build_history
|
15
|
+
|
16
|
+
@job.update(content:)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def content
|
22
|
+
@content ||= @job.content
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_content
|
26
|
+
content.status = :discarded
|
27
|
+
content.finished_at = Time.now
|
28
|
+
content.duration = duration
|
29
|
+
end
|
30
|
+
|
31
|
+
def build_history
|
32
|
+
content.history.push({
|
33
|
+
status: :discarded,
|
34
|
+
date: Time.now
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
def duration
|
39
|
+
return 0 if content.started_at.nil?
|
40
|
+
|
41
|
+
(content.finished_at - Time.parse(content.started_at)).in_milliseconds.round
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module RailsLiveDashboard
|
2
|
+
module Recorders
|
3
|
+
module Jobs
|
4
|
+
class EnqueueRecorder
|
5
|
+
def initialize(event)
|
6
|
+
@event = event
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute
|
10
|
+
Job.create(
|
11
|
+
batch_id: Current.batch_id,
|
12
|
+
content: build_content
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def build_content
|
19
|
+
{
|
20
|
+
job_name: @event.payload[:job].class,
|
21
|
+
job_id: @event.payload[:job].job_id,
|
22
|
+
params: @event.payload[:job].arguments || {},
|
23
|
+
status: :enqueued,
|
24
|
+
queue_name: @event.payload[:job].queue_name,
|
25
|
+
history: build_history
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_history
|
30
|
+
[
|
31
|
+
{
|
32
|
+
status: :enqueued,
|
33
|
+
date: Time.now
|
34
|
+
}
|
35
|
+
]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module RailsLiveDashboard
|
2
|
+
module Recorders
|
3
|
+
module Jobs
|
4
|
+
class PerformRecorder
|
5
|
+
def initialize(event)
|
6
|
+
@event = event
|
7
|
+
@job = Job.same(@event.payload[:job].job_id).first
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
raise Exceptions::EntryNotFound.new(:job, @event.payload[:job].job_id) if @job.nil?
|
12
|
+
|
13
|
+
build_content
|
14
|
+
build_history
|
15
|
+
|
16
|
+
@job.update(content:)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def content
|
22
|
+
@content ||= @job.content
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_content
|
26
|
+
content.status = :performed
|
27
|
+
content.finished_at = Time.now
|
28
|
+
content.duration = duration
|
29
|
+
end
|
30
|
+
|
31
|
+
def build_history
|
32
|
+
content.history.push({
|
33
|
+
status: :performed,
|
34
|
+
date: Time.now
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
def duration
|
39
|
+
return 0 if content.started_at.nil?
|
40
|
+
|
41
|
+
(content.finished_at - Time.parse(content.started_at)).in_milliseconds.round
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module RailsLiveDashboard
|
2
|
+
module Recorders
|
3
|
+
module Jobs
|
4
|
+
class StartRecorder
|
5
|
+
def initialize(event)
|
6
|
+
@event = event
|
7
|
+
@job = Job.same(@event.payload[:job].job_id).first
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
raise Exceptions::EntryNotFound.new(:job, @event.payload[:job].job_id) if @job.nil?
|
12
|
+
|
13
|
+
Current.batch_id = @job.batch_id
|
14
|
+
|
15
|
+
build_content
|
16
|
+
build_history
|
17
|
+
|
18
|
+
@job.update(content:)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def content
|
24
|
+
@content ||= @job.content
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_content
|
28
|
+
content.status = :started
|
29
|
+
content.started_at = Time.now
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_history
|
33
|
+
content.history.push({
|
34
|
+
status: :started,
|
35
|
+
date: Time.now
|
36
|
+
})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module RailsLiveDashboard
|
2
|
+
module Subscribers
|
3
|
+
class ActiveJobSubscriber
|
4
|
+
def initialize
|
5
|
+
enqueue
|
6
|
+
start
|
7
|
+
perform
|
8
|
+
discard
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def enqueue
|
14
|
+
ActiveSupport::Notifications.subscribe 'enqueue_at.active_job' do |event|
|
15
|
+
Recorders::Jobs::EnqueueRecorder.new(event).execute
|
16
|
+
rescue StandardError => e
|
17
|
+
Rails.logger.error "ActiveJobSubscriber - Error on handle enqueue job event: #{e.message}"
|
18
|
+
end
|
19
|
+
|
20
|
+
ActiveSupport::Notifications.subscribe 'enqueue.active_job' do |event|
|
21
|
+
Recorders::Jobs::EnqueueRecorder.new(event).execute
|
22
|
+
rescue StandardError => e
|
23
|
+
Rails.logger.error "ActiveJobSubscriber - Error on handle enqueue job event: #{e.message}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
ActiveSupport::Notifications.subscribe 'perform_start.active_job' do |event|
|
29
|
+
Recorders::Jobs::StartRecorder.new(event).execute
|
30
|
+
rescue StandardError => e
|
31
|
+
Rails.logger.error "ActiveJobSubscriber - Error on handle perform_start job event: #{e.message}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def perform
|
36
|
+
ActiveSupport::Notifications.subscribe 'perform.active_job' do |event|
|
37
|
+
Recorders::Jobs::PerformRecorder.new(event).execute
|
38
|
+
rescue StandardError => e
|
39
|
+
Rails.logger.error "ActiveJobSubscriber - Error on handle perform job event: #{e.message}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def discard
|
44
|
+
ActiveSupport::Notifications.subscribe 'discard.active_job' do |event|
|
45
|
+
Recorders::Jobs::DiscardRecorder.new(event).execute
|
46
|
+
rescue StandardError => e
|
47
|
+
Rails.logger.error "ActiveJobSubscriber - Error on handle discard job event: #{e.message}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/rails_live_dashboard.rb
CHANGED
@@ -2,11 +2,17 @@ require 'rails_live_dashboard/version'
|
|
2
2
|
require 'rails_live_dashboard/engine'
|
3
3
|
require 'rails_live_dashboard/configuration'
|
4
4
|
require 'rails_live_dashboard/context'
|
5
|
+
require 'rails_live_dashboard/exceptions/entry_not_found'
|
5
6
|
require 'rails_live_dashboard/recorders/exception_recorder'
|
6
7
|
require 'rails_live_dashboard/recorders/request_recorder'
|
7
8
|
require 'rails_live_dashboard/recorders/query_recorder'
|
9
|
+
require 'rails_live_dashboard/recorders/jobs/discard_recorder'
|
10
|
+
require 'rails_live_dashboard/recorders/jobs/enqueue_recorder'
|
11
|
+
require 'rails_live_dashboard/recorders/jobs/perform_recorder'
|
12
|
+
require 'rails_live_dashboard/recorders/jobs/start_recorder'
|
8
13
|
require 'rails_live_dashboard/subscribers/action_controller_subscriber'
|
9
14
|
require 'rails_live_dashboard/subscribers/active_record_subscriber'
|
15
|
+
require 'rails_live_dashboard/subscribers/active_job_subscriber'
|
10
16
|
|
11
17
|
module RailsLiveDashboard
|
12
18
|
def self.configuration
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_live_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
|
- Anderson Dalmina
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cssbundling-rails
|
@@ -169,6 +169,8 @@ files:
|
|
169
169
|
- app/assets/javascripts/rails_live_dashboard/controllers/reveal_controller.js
|
170
170
|
- app/assets/javascripts/rails_live_dashboard/controllers/tabs_controller.js
|
171
171
|
- app/assets/stylesheets/rails_live_dashboard/application.css
|
172
|
+
- app/components/rails_live_dashboard/job_status_badge_component.html.erb
|
173
|
+
- app/components/rails_live_dashboard/job_status_badge_component.rb
|
172
174
|
- app/components/rails_live_dashboard/query_duration_badge_component.html.erb
|
173
175
|
- app/components/rails_live_dashboard/query_duration_badge_component.rb
|
174
176
|
- app/components/rails_live_dashboard/request_duration_badge_component.html.erb
|
@@ -181,6 +183,7 @@ files:
|
|
181
183
|
- app/controllers/rails_live_dashboard/clean_controller.rb
|
182
184
|
- app/controllers/rails_live_dashboard/dashboard_controller.rb
|
183
185
|
- app/controllers/rails_live_dashboard/exceptions_controller.rb
|
186
|
+
- app/controllers/rails_live_dashboard/jobs_controller.rb
|
184
187
|
- app/controllers/rails_live_dashboard/queries_controller.rb
|
185
188
|
- app/controllers/rails_live_dashboard/requests_controller.rb
|
186
189
|
- app/controllers/rails_live_dashboard/widgets/slowest_queries_controller.rb
|
@@ -189,16 +192,20 @@ files:
|
|
189
192
|
- app/helpers/rails_live_dashboard/clean_helper.rb
|
190
193
|
- app/helpers/rails_live_dashboard/dashboard_helper.rb
|
191
194
|
- app/helpers/rails_live_dashboard/exceptions_helper.rb
|
195
|
+
- app/helpers/rails_live_dashboard/jobs_helper.rb
|
192
196
|
- app/helpers/rails_live_dashboard/requests_helper.rb
|
193
197
|
- app/jobs/rails_live_dashboard/application_job.rb
|
194
198
|
- app/mailers/rails_live_dashboard/application_mailer.rb
|
195
199
|
- app/models/rails_live_dashboard/application_record.rb
|
200
|
+
- app/models/rails_live_dashboard/current.rb
|
196
201
|
- app/models/rails_live_dashboard/entry.rb
|
197
202
|
- app/models/rails_live_dashboard/exception.rb
|
203
|
+
- app/models/rails_live_dashboard/job.rb
|
198
204
|
- app/models/rails_live_dashboard/query.rb
|
199
205
|
- app/models/rails_live_dashboard/request.rb
|
200
206
|
- app/models/rails_live_dashboard/types/content.rb
|
201
207
|
- app/models/rails_live_dashboard/types/exception_content.rb
|
208
|
+
- app/models/rails_live_dashboard/types/job_content.rb
|
202
209
|
- app/models/rails_live_dashboard/types/query_content.rb
|
203
210
|
- app/models/rails_live_dashboard/types/request_content.rb
|
204
211
|
- app/views/layouts/rails_live_dashboard/application.html.erb
|
@@ -206,6 +213,11 @@ files:
|
|
206
213
|
- app/views/rails_live_dashboard/exceptions/_tabs.html.erb
|
207
214
|
- app/views/rails_live_dashboard/exceptions/index.html.erb
|
208
215
|
- app/views/rails_live_dashboard/exceptions/show.html.erb
|
216
|
+
- app/views/rails_live_dashboard/jobs/_queries.html.erb
|
217
|
+
- app/views/rails_live_dashboard/jobs/_relateds.html.erb
|
218
|
+
- app/views/rails_live_dashboard/jobs/_tabs.html.erb
|
219
|
+
- app/views/rails_live_dashboard/jobs/index.html.erb
|
220
|
+
- app/views/rails_live_dashboard/jobs/show.html.erb
|
209
221
|
- app/views/rails_live_dashboard/queries/_list.html.erb
|
210
222
|
- app/views/rails_live_dashboard/queries/_query.html.erb
|
211
223
|
- app/views/rails_live_dashboard/queries/index.html.erb
|
@@ -230,10 +242,16 @@ files:
|
|
230
242
|
- lib/rails_live_dashboard/configuration.rb
|
231
243
|
- lib/rails_live_dashboard/context.rb
|
232
244
|
- lib/rails_live_dashboard/engine.rb
|
245
|
+
- lib/rails_live_dashboard/exceptions/entry_not_found.rb
|
233
246
|
- lib/rails_live_dashboard/recorders/exception_recorder.rb
|
247
|
+
- lib/rails_live_dashboard/recorders/jobs/discard_recorder.rb
|
248
|
+
- lib/rails_live_dashboard/recorders/jobs/enqueue_recorder.rb
|
249
|
+
- lib/rails_live_dashboard/recorders/jobs/perform_recorder.rb
|
250
|
+
- lib/rails_live_dashboard/recorders/jobs/start_recorder.rb
|
234
251
|
- lib/rails_live_dashboard/recorders/query_recorder.rb
|
235
252
|
- lib/rails_live_dashboard/recorders/request_recorder.rb
|
236
253
|
- lib/rails_live_dashboard/subscribers/action_controller_subscriber.rb
|
254
|
+
- lib/rails_live_dashboard/subscribers/active_job_subscriber.rb
|
237
255
|
- lib/rails_live_dashboard/subscribers/active_record_subscriber.rb
|
238
256
|
- lib/rails_live_dashboard/version.rb
|
239
257
|
- lib/tasks/rails_live_dashboard_tasks.rake
|
@@ -259,7 +277,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
259
277
|
- !ruby/object:Gem::Version
|
260
278
|
version: '0'
|
261
279
|
requirements: []
|
262
|
-
rubygems_version: 3.5.
|
280
|
+
rubygems_version: 3.5.3
|
263
281
|
signing_key:
|
264
282
|
specification_version: 4
|
265
283
|
summary: Realtime debug dashboard for Rails applications
|