rails_live_dashboard 0.1.2 → 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/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
|

|
@@ -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
|