pgbus 0.3.9 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/frontend/pgbus/style.css +1 -1
- data/app/frontend/pgbus/tailwind.css +28 -1
- data/app/views/layouts/pgbus/application.html.erb +58 -12
- data/app/views/pgbus/dead_letter/_messages_table.html.erb +2 -3
- data/app/views/pgbus/insights/show.html.erb +6 -6
- data/app/views/pgbus/jobs/_enqueued_table.html.erb +2 -3
- data/lib/pgbus/client.rb +79 -0
- data/lib/pgbus/failed_event_recorder.rb +15 -2
- data/lib/pgbus/process/dispatcher.rb +34 -14
- data/lib/pgbus/process/worker.rb +19 -1
- data/lib/pgbus/uniqueness.rb +10 -5
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source.rb +44 -9
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0f6e053c803e42358c0c3c100dcd24f1d5f3dbfe6087ed1ee95c2ce520643eb
|
|
4
|
+
data.tar.gz: d6cceba94c15d7b7b415ea8643f042f5eacf185cfb48f220be893e5f870affd2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f68ce7714dd52a9389190efa27d5a645870a52214531de33d7dea2242fa6b3b5f62a2e1b5d74deac70cbd65087265a87284008e611d3f1f3b45143fb2b9bf57
|
|
7
|
+
data.tar.gz: ead64910839a86aa82df6ce4b46e2a485c6e3668c4a915a1d308d3b9f7b7e8595a2dead190307ab760c611d75914f28d2cc06832372194f5761148bc213c978d
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! tailwindcss v4.1.7 | MIT License | https://tailwindcss.com */
|
|
2
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-orange-400:oklch(75% .183 55.934);--color-orange-600:oklch(64.6% .222 41.116);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-300:oklch(78.5% .115 274.713);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-indigo-700:oklch(45.7% .24 277.023);--color-indigo-800:oklch(39.8% .195 277.366);--color-indigo-900:oklch(35.9% .144 278.697);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-gray-950:oklch(13% .028 261.692);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height:calc(1.5/1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25/1.875);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-md:.375rem;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.top-4{top:calc(var(--spacing)*4)}.right-0{right:calc(var(--spacing)*0)}.right-4{right:calc(var(--spacing)*4)}.isolate{isolation:isolate}.z-50{z-index:50}.z-\[100\]{z-index:100}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}@media (max-width:1023px){.pgbus-table thead{display:none}.pgbus-table tbody tr{border:1px solid #e5e7eb;border-radius:.5rem;margin-bottom:.75rem;padding:.75rem;display:block}.pgbus-table:where(.dark,.dark *) tbody tr{border-color:#374151}.pgbus-table tbody td{text-align:right;border:none;justify-content:space-between;align-items:baseline;padding:.25rem 0;display:flex}.pgbus-table tbody td:before{content:attr(data-label);text-transform:uppercase;color:#6b7280;text-align:left;flex-shrink:0;margin-right:1rem;font-size:.75rem;font-weight:600}.pgbus-table:where(.dark,.dark *) tbody td:before{color:#9ca3af}.pgbus-table tbody td[colspan]{text-align:center;display:block}.pgbus-table tbody td[colspan]:before{display:none}}.mr-1{margin-right:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.-ml-px{margin-left:-1px}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-10{height:calc(var(--spacing)*10)}.h-14{height:calc(var(--spacing)*14)}.h-full{height:100%}.max-h-40{max-height:calc(var(--spacing)*40)}.max-h-96{max-height:calc(var(--spacing)*96)}.min-h-full{min-height:100%}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-10{width:calc(var(--spacing)*10)}.w-16{width:calc(var(--spacing)*16)}.w-28{width:calc(var(--spacing)*28)}.w-40{width:calc(var(--spacing)*40)}.w-44{width:calc(var(--spacing)*44)}.w-full{width:100%}.max-w-7xl{max-width:var(--container-7xl)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-full{min-width:100%}.flex-1{flex:1}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.origin-top-right{transform-origin:100% 0}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-none{list-style-type:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*1)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*3)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*4)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*8)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}:where(.divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-l-lg{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-r-lg{border-top-right-radius:var(--radius-lg);border-bottom-right-radius:var(--radius-lg)}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-blue-200{border-color:var(--color-blue-200)}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-800{border-color:var(--color-gray-800)}.border-green-200{border-color:var(--color-green-200)}.border-indigo-600{border-color:var(--color-indigo-600)}.border-red-200{border-color:var(--color-red-200)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-700{background-color:var(--color-gray-700)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-900{background-color:var(--color-gray-900)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-600{background-color:var(--color-green-600)}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-600{background-color:var(--color-red-600)}.bg-red-800{background-color:var(--color-red-800)}.bg-white{background-color:var(--color-white)}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-yellow-500{background-color:var(--color-yellow-500)}.p-0{padding:calc(var(--spacing)*0)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.py-12{padding-block:calc(var(--spacing)*12)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-24{padding-top:calc(var(--spacing)*24)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-900{color:var(--color-gray-900)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-green-800{color:var(--color-green-800)}.text-indigo-600{color:var(--color-indigo-600)}.text-indigo-700{color:var(--color-indigo-700)}.text-orange-600{color:var(--color-orange-600)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-white{color:var(--color-white)}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-700{color:var(--color-yellow-700)}.text-yellow-800{color:var(--color-yellow-800)}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-black\/5{--tw-ring-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.ring-black\/5{--tw-ring-color:color-mix(in oklab,var(--color-black)5%,transparent)}}.ring-gray-200{--tw-ring-color:var(--color-gray-200)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.backdrop\:bg-gray-900\/50::backdrop{background-color:#10182880}@supports (color:color-mix(in lab, red, red)){.backdrop\:bg-gray-900\/50::backdrop{background-color:color-mix(in oklab,var(--color-gray-900)50%,transparent)}}@media (hover:hover){.hover\:bg-blue-200:hover{background-color:var(--color-blue-200)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-200:hover{background-color:var(--color-gray-200)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:bg-green-200:hover{background-color:var(--color-green-200)}.hover\:bg-green-500:hover{background-color:var(--color-green-500)}.hover\:bg-indigo-500:hover{background-color:var(--color-indigo-500)}.hover\:bg-red-500:hover{background-color:var(--color-red-500)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:bg-yellow-200:hover{background-color:var(--color-yellow-200)}.hover\:bg-yellow-400:hover{background-color:var(--color-yellow-400)}.hover\:text-blue-700:hover{color:var(--color-blue-700)}.hover\:text-blue-800:hover{color:var(--color-blue-800)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-green-800:hover{color:var(--color-green-800)}.hover\:text-indigo-500:hover{color:var(--color-indigo-500)}.hover\:text-indigo-800:hover{color:var(--color-indigo-800)}.hover\:text-red-800:hover{color:var(--color-red-800)}.hover\:text-white:hover{color:var(--color-white)}.hover\:text-yellow-800:hover{color:var(--color-yellow-800)}}.focus\:z-10:focus{z-index:10}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-indigo-500:focus{--tw-ring-color:var(--color-indigo-500)}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-yellow-500:focus{--tw-ring-color:var(--color-yellow-500)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:ring-offset-gray-900:focus{--tw-ring-offset-color:var(--color-gray-900)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}@media (min-width:40rem){.sm\:col-span-2{grid-column:span 2/span 2}.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\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:px-6{padding-inline:calc(var(--spacing)*6)}}@media (min-width:64rem){.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:px-8{padding-inline:calc(var(--spacing)*8)}}.dark\:block:where(.dark,.dark *){display:block}.dark\:hidden:where(.dark,.dark *){display:none}:where(.dark\:divide-gray-700:where(.dark,.dark *)>:not(:last-child)){border-color:var(--color-gray-700)}.dark\:border-blue-800:where(.dark,.dark *){border-color:var(--color-blue-800)}.dark\:border-gray-600:where(.dark,.dark *){border-color:var(--color-gray-600)}.dark\:border-gray-700:where(.dark,.dark *){border-color:var(--color-gray-700)}.dark\:border-green-800:where(.dark,.dark *){border-color:var(--color-green-800)}.dark\:border-indigo-500:where(.dark,.dark *){border-color:var(--color-indigo-500)}.dark\:border-red-800:where(.dark,.dark *){border-color:var(--color-red-800)}.dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:#1c398e4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-blue-900)30%,transparent)}}.dark\:bg-blue-900\/50:where(.dark,.dark *){background-color:#1c398e80}@supports (color:color-mix(in lab, red, red)){.dark\:bg-blue-900\/50:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-blue-900)50%,transparent)}}.dark\:bg-gray-700:where(.dark,.dark *){background-color:var(--color-gray-700)}.dark\:bg-gray-800:where(.dark,.dark *){background-color:var(--color-gray-800)}.dark\:bg-gray-900:where(.dark,.dark *){background-color:var(--color-gray-900)}.dark\:bg-gray-900\/50:where(.dark,.dark *){background-color:#10182880}@supports (color:color-mix(in lab, red, red)){.dark\:bg-gray-900\/50:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-gray-900)50%,transparent)}}.dark\:bg-gray-950:where(.dark,.dark *){background-color:var(--color-gray-950)}.dark\:bg-green-900\/30:where(.dark,.dark *){background-color:#0d542b4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-green-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-green-900)30%,transparent)}}.dark\:bg-indigo-900\/30:where(.dark,.dark *){background-color:#312c854d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-indigo-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-indigo-900)30%,transparent)}}.dark\:bg-red-900\/30:where(.dark,.dark *){background-color:#82181a4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-red-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-red-900)30%,transparent)}}.dark\:bg-yellow-900\/30:where(.dark,.dark *){background-color:#733e0a4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-yellow-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-yellow-900)30%,transparent)}}.dark\:bg-yellow-900\/50:where(.dark,.dark *){background-color:#733e0a80}@supports (color:color-mix(in lab, red, red)){.dark\:bg-yellow-900\/50:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-yellow-900)50%,transparent)}}.dark\:text-blue-300:where(.dark,.dark *){color:var(--color-blue-300)}.dark\:text-blue-400:where(.dark,.dark *){color:var(--color-blue-400)}.dark\:text-gray-300:where(.dark,.dark *){color:var(--color-gray-300)}.dark\:text-gray-400:where(.dark,.dark *){color:var(--color-gray-400)}.dark\:text-gray-500:where(.dark,.dark *){color:var(--color-gray-500)}.dark\:text-green-300:where(.dark,.dark *){color:var(--color-green-300)}.dark\:text-green-400:where(.dark,.dark *){color:var(--color-green-400)}.dark\:text-indigo-300:where(.dark,.dark *){color:var(--color-indigo-300)}.dark\:text-orange-400:where(.dark,.dark *){color:var(--color-orange-400)}.dark\:text-red-300:where(.dark,.dark *){color:var(--color-red-300)}.dark\:text-red-400:where(.dark,.dark *){color:var(--color-red-400)}.dark\:text-white:where(.dark,.dark *){color:var(--color-white)}.dark\:text-yellow-300:where(.dark,.dark *){color:var(--color-yellow-300)}.dark\:ring-gray-700:where(.dark,.dark *){--tw-ring-color:var(--color-gray-700)}.dark\:ring-white\/10:where(.dark,.dark *){--tw-ring-color:#ffffff1a}@supports (color:color-mix(in lab, red, red)){.dark\:ring-white\/10:where(.dark,.dark *){--tw-ring-color:color-mix(in oklab,var(--color-white)10%,transparent)}}@media (hover:hover){.dark\:hover\:bg-gray-600:where(.dark,.dark *):hover{background-color:var(--color-gray-600)}.dark\:hover\:bg-gray-700:where(.dark,.dark *):hover{background-color:var(--color-gray-700)}.dark\:hover\:bg-gray-700\/50:where(.dark,.dark *):hover{background-color:#36415380}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-gray-700\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--color-gray-700)50%,transparent)}}}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-orange-400:oklch(75% .183 55.934);--color-orange-600:oklch(64.6% .222 41.116);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-300:oklch(78.5% .115 274.713);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-indigo-700:oklch(45.7% .24 277.023);--color-indigo-800:oklch(39.8% .195 277.366);--color-indigo-900:oklch(35.9% .144 278.697);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-gray-950:oklch(13% .028 261.692);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height:calc(1.5/1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25/1.875);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-md:.375rem;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.top-4{top:calc(var(--spacing)*4)}.right-0{right:calc(var(--spacing)*0)}.right-4{right:calc(var(--spacing)*4)}.isolate{isolation:isolate}.z-50{z-index:50}.z-\[100\]{z-index:100}.order-42{order:42}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mr-1{margin-right:calc(var(--spacing)*1)}@media (max-width:1023px){.pgbus-table{box-sizing:border-box;width:100%;max-width:100%;display:block}.pgbus-table thead{display:none}.pgbus-table tbody{width:100%;display:block}.pgbus-table tbody tr{box-sizing:border-box;overflow-wrap:anywhere;border:1px solid #e5e7eb;border-radius:.5rem;width:100%;max-width:100%;margin-bottom:.75rem;padding:.75rem;display:block}.pgbus-table:where(.dark,.dark *) tbody tr{border-color:#374151}.pgbus-table tbody td{box-sizing:border-box;text-align:right;border:none;justify-content:space-between;align-items:baseline;gap:1rem;width:100%;min-width:0;max-width:100%;padding:.25rem 0;display:flex}.pgbus-table tbody td>*{overflow-wrap:anywhere;min-width:0;max-width:100%}.pgbus-table tbody td:before{content:attr(data-label);text-transform:uppercase;color:#6b7280;text-align:left;flex-shrink:0;font-size:.75rem;font-weight:600}.pgbus-table:where(.dark,.dark *) tbody td:before{color:#9ca3af}.pgbus-table tbody td[colspan]{text-align:center;display:block}.pgbus-table tbody td[colspan]:before{display:none}}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.-ml-px{margin-left:-1px}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-10{height:calc(var(--spacing)*10)}.h-14{height:calc(var(--spacing)*14)}.h-full{height:100%}.max-h-40{max-height:calc(var(--spacing)*40)}.max-h-96{max-height:calc(var(--spacing)*96)}.min-h-full{min-height:100%}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-10{width:calc(var(--spacing)*10)}.w-16{width:calc(var(--spacing)*16)}.w-28{width:calc(var(--spacing)*28)}.w-40{width:calc(var(--spacing)*40)}.w-44{width:calc(var(--spacing)*44)}.w-full{width:100%}.max-w-7xl{max-width:var(--container-7xl)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-full{min-width:100%}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.origin-top-right{transform-origin:100% 0}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-none{list-style-type:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*1)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*3)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*4)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*8)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}:where(.divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-l-lg{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-r-lg{border-top-right-radius:var(--radius-lg);border-bottom-right-radius:var(--radius-lg)}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-blue-200{border-color:var(--color-blue-200)}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-800{border-color:var(--color-gray-800)}.border-green-200{border-color:var(--color-green-200)}.border-indigo-600{border-color:var(--color-indigo-600)}.border-red-200{border-color:var(--color-red-200)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-700{background-color:var(--color-gray-700)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-900{background-color:var(--color-gray-900)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-600{background-color:var(--color-green-600)}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-600{background-color:var(--color-red-600)}.bg-red-800{background-color:var(--color-red-800)}.bg-white{background-color:var(--color-white)}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-yellow-500{background-color:var(--color-yellow-500)}.p-0{padding:calc(var(--spacing)*0)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.py-12{padding-block:calc(var(--spacing)*12)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-24{padding-top:calc(var(--spacing)*24)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-900{color:var(--color-gray-900)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-green-800{color:var(--color-green-800)}.text-indigo-600{color:var(--color-indigo-600)}.text-indigo-700{color:var(--color-indigo-700)}.text-orange-600{color:var(--color-orange-600)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-white{color:var(--color-white)}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-700{color:var(--color-yellow-700)}.text-yellow-800{color:var(--color-yellow-800)}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-black\/5{--tw-ring-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.ring-black\/5{--tw-ring-color:color-mix(in oklab,var(--color-black)5%,transparent)}}.ring-gray-200{--tw-ring-color:var(--color-gray-200)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.backdrop\:bg-gray-900\/50::backdrop{background-color:#10182880}@supports (color:color-mix(in lab, red, red)){.backdrop\:bg-gray-900\/50::backdrop{background-color:color-mix(in oklab,var(--color-gray-900)50%,transparent)}}@media (hover:hover){.hover\:bg-blue-200:hover{background-color:var(--color-blue-200)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-200:hover{background-color:var(--color-gray-200)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:bg-green-200:hover{background-color:var(--color-green-200)}.hover\:bg-green-500:hover{background-color:var(--color-green-500)}.hover\:bg-indigo-500:hover{background-color:var(--color-indigo-500)}.hover\:bg-red-500:hover{background-color:var(--color-red-500)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:bg-yellow-200:hover{background-color:var(--color-yellow-200)}.hover\:bg-yellow-400:hover{background-color:var(--color-yellow-400)}.hover\:text-blue-700:hover{color:var(--color-blue-700)}.hover\:text-blue-800:hover{color:var(--color-blue-800)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-green-800:hover{color:var(--color-green-800)}.hover\:text-indigo-500:hover{color:var(--color-indigo-500)}.hover\:text-indigo-800:hover{color:var(--color-indigo-800)}.hover\:text-red-800:hover{color:var(--color-red-800)}.hover\:text-white:hover{color:var(--color-white)}.hover\:text-yellow-800:hover{color:var(--color-yellow-800)}}.focus\:z-10:focus{z-index:10}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-indigo-500:focus{--tw-ring-color:var(--color-indigo-500)}.focus\:ring-indigo-600:focus{--tw-ring-color:var(--color-indigo-600)}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-yellow-500:focus{--tw-ring-color:var(--color-yellow-500)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:ring-offset-gray-900:focus{--tw-ring-offset-color:var(--color-gray-900)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}@media (min-width:40rem){.sm\:col-span-2{grid-column:span 2/span 2}.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\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:px-6{padding-inline:calc(var(--spacing)*6)}}@media (min-width:64rem){.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:px-8{padding-inline:calc(var(--spacing)*8)}}.dark\:block:where(.dark,.dark *){display:block}.dark\:hidden:where(.dark,.dark *){display:none}:where(.dark\:divide-gray-700:where(.dark,.dark *)>:not(:last-child)){border-color:var(--color-gray-700)}.dark\:border-blue-800:where(.dark,.dark *){border-color:var(--color-blue-800)}.dark\:border-gray-600:where(.dark,.dark *){border-color:var(--color-gray-600)}.dark\:border-gray-700:where(.dark,.dark *){border-color:var(--color-gray-700)}.dark\:border-green-800:where(.dark,.dark *){border-color:var(--color-green-800)}.dark\:border-indigo-500:where(.dark,.dark *){border-color:var(--color-indigo-500)}.dark\:border-red-800:where(.dark,.dark *){border-color:var(--color-red-800)}.dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:#1c398e4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-blue-900)30%,transparent)}}.dark\:bg-gray-700:where(.dark,.dark *){background-color:var(--color-gray-700)}.dark\:bg-gray-800:where(.dark,.dark *){background-color:var(--color-gray-800)}.dark\:bg-gray-900:where(.dark,.dark *){background-color:var(--color-gray-900)}.dark\:bg-gray-900\/50:where(.dark,.dark *){background-color:#10182880}@supports (color:color-mix(in lab, red, red)){.dark\:bg-gray-900\/50:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-gray-900)50%,transparent)}}.dark\:bg-gray-950:where(.dark,.dark *){background-color:var(--color-gray-950)}.dark\:bg-green-900\/30:where(.dark,.dark *){background-color:#0d542b4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-green-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-green-900)30%,transparent)}}.dark\:bg-indigo-900\/30:where(.dark,.dark *){background-color:#312c854d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-indigo-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-indigo-900)30%,transparent)}}.dark\:bg-red-900\/30:where(.dark,.dark *){background-color:#82181a4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-red-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-red-900)30%,transparent)}}.dark\:bg-yellow-900\/30:where(.dark,.dark *){background-color:#733e0a4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-yellow-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-yellow-900)30%,transparent)}}.dark\:text-blue-300:where(.dark,.dark *){color:var(--color-blue-300)}.dark\:text-blue-400:where(.dark,.dark *){color:var(--color-blue-400)}.dark\:text-gray-300:where(.dark,.dark *){color:var(--color-gray-300)}.dark\:text-gray-400:where(.dark,.dark *){color:var(--color-gray-400)}.dark\:text-gray-500:where(.dark,.dark *){color:var(--color-gray-500)}.dark\:text-green-300:where(.dark,.dark *){color:var(--color-green-300)}.dark\:text-green-400:where(.dark,.dark *){color:var(--color-green-400)}.dark\:text-indigo-300:where(.dark,.dark *){color:var(--color-indigo-300)}.dark\:text-orange-400:where(.dark,.dark *){color:var(--color-orange-400)}.dark\:text-red-300:where(.dark,.dark *){color:var(--color-red-300)}.dark\:text-red-400:where(.dark,.dark *){color:var(--color-red-400)}.dark\:text-white:where(.dark,.dark *){color:var(--color-white)}.dark\:text-yellow-400:where(.dark,.dark *){color:var(--color-yellow-400)}.dark\:ring-gray-700:where(.dark,.dark *){--tw-ring-color:var(--color-gray-700)}.dark\:ring-white\/10:where(.dark,.dark *){--tw-ring-color:#ffffff1a}@supports (color:color-mix(in lab, red, red)){.dark\:ring-white\/10:where(.dark,.dark *){--tw-ring-color:color-mix(in oklab,var(--color-white)10%,transparent)}}@media (hover:hover){.dark\:hover\:bg-gray-600:where(.dark,.dark *):hover{background-color:var(--color-gray-600)}.dark\:hover\:bg-gray-700:where(.dark,.dark *):hover{background-color:var(--color-gray-700)}.dark\:hover\:bg-gray-700\/50:where(.dark,.dark *):hover{background-color:#36415380}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-gray-700\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--color-gray-700)50%,transparent)}}}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}
|
|
@@ -4,18 +4,34 @@
|
|
|
4
4
|
|
|
5
5
|
/* Responsive tables: stack rows as cards on small screens */
|
|
6
6
|
@utility pgbus-table {
|
|
7
|
+
@media (max-width: 1023px) {
|
|
8
|
+
display: block;
|
|
9
|
+
width: 100%;
|
|
10
|
+
max-width: 100%;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
7
13
|
& thead {
|
|
8
14
|
@media (max-width: 1023px) {
|
|
9
15
|
display: none;
|
|
10
16
|
}
|
|
11
17
|
}
|
|
18
|
+
& tbody {
|
|
19
|
+
@media (max-width: 1023px) {
|
|
20
|
+
display: block;
|
|
21
|
+
width: 100%;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
12
24
|
& tbody tr {
|
|
13
25
|
@media (max-width: 1023px) {
|
|
14
26
|
display: block;
|
|
27
|
+
width: 100%;
|
|
28
|
+
max-width: 100%;
|
|
29
|
+
box-sizing: border-box;
|
|
15
30
|
margin-bottom: 0.75rem;
|
|
16
31
|
border-radius: 0.5rem;
|
|
17
32
|
padding: 0.75rem;
|
|
18
33
|
border: 1px solid #e5e7eb;
|
|
34
|
+
overflow-wrap: anywhere;
|
|
19
35
|
}
|
|
20
36
|
}
|
|
21
37
|
&:where(.dark, .dark *) tbody tr {
|
|
@@ -26,11 +42,23 @@
|
|
|
26
42
|
& tbody td {
|
|
27
43
|
@media (max-width: 1023px) {
|
|
28
44
|
display: flex;
|
|
45
|
+
width: 100%;
|
|
46
|
+
max-width: 100%;
|
|
47
|
+
box-sizing: border-box;
|
|
29
48
|
justify-content: space-between;
|
|
30
49
|
align-items: baseline;
|
|
50
|
+
gap: 1rem;
|
|
31
51
|
padding: 0.25rem 0;
|
|
32
52
|
border: none;
|
|
33
53
|
text-align: right;
|
|
54
|
+
min-width: 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
& tbody td > * {
|
|
58
|
+
@media (max-width: 1023px) {
|
|
59
|
+
min-width: 0;
|
|
60
|
+
max-width: 100%;
|
|
61
|
+
overflow-wrap: anywhere;
|
|
34
62
|
}
|
|
35
63
|
}
|
|
36
64
|
& tbody td::before {
|
|
@@ -41,7 +69,6 @@
|
|
|
41
69
|
text-transform: uppercase;
|
|
42
70
|
color: #6b7280;
|
|
43
71
|
text-align: left;
|
|
44
|
-
margin-right: 1rem;
|
|
45
72
|
flex-shrink: 0;
|
|
46
73
|
}
|
|
47
74
|
}
|
|
@@ -13,23 +13,69 @@
|
|
|
13
13
|
(!localStorage.getItem('pgbus-dark') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
14
14
|
document.documentElement.classList.add('dark');
|
|
15
15
|
}
|
|
16
|
-
function
|
|
16
|
+
function pgbusToggleDarkMode() {
|
|
17
17
|
var isDark = document.documentElement.classList.toggle('dark');
|
|
18
18
|
localStorage.setItem('pgbus-dark', isDark);
|
|
19
19
|
}
|
|
20
|
-
function
|
|
20
|
+
function pgbusSyncAriaExpanded(menuId, expanded) {
|
|
21
|
+
var btn = document.querySelector('[aria-controls="' + menuId + '"]');
|
|
22
|
+
if (btn) btn.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
|
23
|
+
}
|
|
24
|
+
function pgbusToggleMobileMenu() {
|
|
21
25
|
var menu = document.getElementById('pgbus-mobile-menu');
|
|
22
26
|
var openIcon = document.getElementById('pgbus-menu-open');
|
|
23
27
|
var closeIcon = document.getElementById('pgbus-menu-close');
|
|
24
|
-
menu
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
if (!menu) return;
|
|
29
|
+
var isOpen = menu.classList.toggle('hidden') === false;
|
|
30
|
+
if (openIcon) openIcon.classList.toggle('hidden');
|
|
31
|
+
if (closeIcon) closeIcon.classList.toggle('hidden');
|
|
32
|
+
pgbusSyncAriaExpanded('pgbus-mobile-menu', isOpen);
|
|
33
|
+
}
|
|
34
|
+
function pgbusToggleLocaleMenu() {
|
|
35
|
+
var menu = document.getElementById('pgbus-locale-menu');
|
|
36
|
+
if (!menu) return;
|
|
37
|
+
var isOpen = menu.classList.toggle('hidden') === false;
|
|
38
|
+
pgbusSyncAriaExpanded('pgbus-locale-menu', isOpen);
|
|
27
39
|
}
|
|
40
|
+
function pgbusBulkRowToggle(checkbox) {
|
|
41
|
+
var first = checkbox.nextElementSibling;
|
|
42
|
+
var second = first && first.nextElementSibling;
|
|
43
|
+
if (first) first.disabled = !checkbox.checked;
|
|
44
|
+
if (second) second.disabled = !checkbox.checked;
|
|
45
|
+
}
|
|
46
|
+
function pgbusBindActions(root) {
|
|
47
|
+
(root || document).querySelectorAll('[data-pgbus-action]').forEach(function(el) {
|
|
48
|
+
if (el.dataset.pgbusBound === 'true') return;
|
|
49
|
+
el.dataset.pgbusBound = 'true';
|
|
50
|
+
var action = el.dataset.pgbusAction;
|
|
51
|
+
if (action === 'toggle-dark-mode') {
|
|
52
|
+
el.addEventListener('click', pgbusToggleDarkMode);
|
|
53
|
+
} else if (action === 'toggle-mobile-menu') {
|
|
54
|
+
el.addEventListener('click', pgbusToggleMobileMenu);
|
|
55
|
+
} else if (action === 'toggle-locale-menu') {
|
|
56
|
+
el.addEventListener('click', pgbusToggleLocaleMenu);
|
|
57
|
+
} else if (action === 'bulk-row-toggle') {
|
|
58
|
+
el.addEventListener('change', function() { pgbusBulkRowToggle(el); });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
document.addEventListener('DOMContentLoaded', function() { pgbusBindActions(); });
|
|
63
|
+
document.addEventListener('turbo:load', function() { pgbusBindActions(); });
|
|
64
|
+
document.addEventListener('turbo:frame-load', function(e) { pgbusBindActions(e.target); });
|
|
65
|
+
<%# Turbo Drive caches pages via cloneNode(true), which keeps attributes
|
|
66
|
+
but drops event listeners. Clear the binding sentinel before caching
|
|
67
|
+
so pgbusBindActions() actually rebinds when the snapshot is restored. %>
|
|
68
|
+
document.addEventListener('turbo:before-cache', function() {
|
|
69
|
+
document.querySelectorAll('[data-pgbus-bound]').forEach(function(el) {
|
|
70
|
+
el.removeAttribute('data-pgbus-bound');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
28
73
|
document.addEventListener('click', function(e) {
|
|
29
74
|
var switcher = document.getElementById('pgbus-locale-switcher');
|
|
30
75
|
var menu = document.getElementById('pgbus-locale-menu');
|
|
31
|
-
if (menu && switcher && !switcher.contains(e.target)) {
|
|
76
|
+
if (menu && switcher && !switcher.contains(e.target) && !menu.classList.contains('hidden')) {
|
|
32
77
|
menu.classList.add('hidden');
|
|
78
|
+
pgbusSyncAriaExpanded('pgbus-locale-menu', false);
|
|
33
79
|
}
|
|
34
80
|
});
|
|
35
81
|
</script>
|
|
@@ -37,9 +83,9 @@
|
|
|
37
83
|
<%# Self-hosted assets — no external CDN dependencies %>
|
|
38
84
|
<%= tag.link rel: "stylesheet", href: frontend_static_path(:style, format: :css, locale: nil), nonce: content_security_policy_nonce %>
|
|
39
85
|
|
|
40
|
-
<%# Importmap for ES modules %>
|
|
86
|
+
<%# Importmap for ES modules — keys are internal symbols, values are app-controlled paths, no user input %>
|
|
41
87
|
<% importmaps = Pgbus::FrontendsController.js_modules.keys.index_with { |mod| frontend_module_path(mod, format: :js, locale: nil) } %>
|
|
42
|
-
<%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce) %>
|
|
88
|
+
<%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce) %> <%# herb:disable erb-no-unsafe-raw %>
|
|
43
89
|
<%= tag.script("", src: frontend_static_path(:apexcharts, format: :js, locale: nil), nonce: content_security_policy_nonce) %>
|
|
44
90
|
|
|
45
91
|
<script type="module" nonce="<%= content_security_policy_nonce %>">
|
|
@@ -94,7 +140,7 @@
|
|
|
94
140
|
<div class="flex items-center space-x-2">
|
|
95
141
|
<!-- Locale switcher -->
|
|
96
142
|
<div class="relative" id="pgbus-locale-switcher">
|
|
97
|
-
<button type="button"
|
|
143
|
+
<button type="button" data-pgbus-action="toggle-locale-menu" aria-controls="pgbus-locale-menu" aria-expanded="false" aria-haspopup="true" class="rounded-md px-2 py-1 text-sm text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
98
144
|
<%= pgbus_locale_flag(I18n.locale) %> <%= I18n.locale.to_s.upcase %>
|
|
99
145
|
</button>
|
|
100
146
|
<div id="pgbus-locale-menu" class="hidden absolute right-0 z-50 mt-1 w-44 origin-top-right rounded-md bg-white dark:bg-gray-800 shadow-lg ring-1 ring-black/5 dark:ring-white/10">
|
|
@@ -109,7 +155,7 @@
|
|
|
109
155
|
</div>
|
|
110
156
|
|
|
111
157
|
<!-- Dark mode toggle -->
|
|
112
|
-
<button
|
|
158
|
+
<button type="button" data-pgbus-action="toggle-dark-mode" class="rounded-md p-2 text-gray-400 hover:text-white focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-900" aria-label="<%= t("pgbus.layout.toggle_dark_mode") %>">
|
|
113
159
|
<svg class="h-5 w-5 hidden dark:block" fill="currentColor" viewBox="0 0 20 20">
|
|
114
160
|
<path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"/>
|
|
115
161
|
</svg>
|
|
@@ -119,7 +165,7 @@
|
|
|
119
165
|
</button>
|
|
120
166
|
|
|
121
167
|
<!-- Mobile menu button (hidden on large screens) -->
|
|
122
|
-
<button
|
|
168
|
+
<button type="button" data-pgbus-action="toggle-mobile-menu" aria-controls="pgbus-mobile-menu" aria-expanded="false" class="lg:hidden rounded-md p-2 text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500" aria-label="<%= t("pgbus.layout.toggle_menu") %>">
|
|
123
169
|
<!-- Hamburger icon -->
|
|
124
170
|
<svg id="pgbus-menu-open" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
|
125
171
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"/>
|
|
@@ -171,7 +217,7 @@
|
|
|
171
217
|
</svg>
|
|
172
218
|
</div>
|
|
173
219
|
<div class="flex-1">
|
|
174
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-white" id="pgbus-confirm-title"
|
|
220
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white" id="pgbus-confirm-title"><%= t("pgbus.dialogs.confirm_title", default: "Are you sure?") %></h3>
|
|
175
221
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300" id="pgbus-confirm-message"></p>
|
|
176
222
|
</div>
|
|
177
223
|
</div>
|
|
@@ -28,11 +28,10 @@
|
|
|
28
28
|
<% source_queue = m[:queue_name].to_s.delete_suffix(dlq_suffix) %>
|
|
29
29
|
<tr>
|
|
30
30
|
<td class="w-10 px-4 py-3 align-top">
|
|
31
|
-
<input type="checkbox" data-bulk-item
|
|
31
|
+
<input type="checkbox" data-bulk-item data-pgbus-action="bulk-row-toggle"
|
|
32
32
|
aria-label="<%= t("pgbus.helpers.bulk_select_row", id: m[:msg_id]) %>"
|
|
33
33
|
data-queue-name="<%= m[:queue_name] %>" data-msg-id="<%= m[:msg_id] %>"
|
|
34
|
-
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600 mt-1"
|
|
35
|
-
onchange="this.nextElementSibling.disabled = !this.checked; this.nextElementSibling.nextElementSibling.disabled = !this.checked">
|
|
34
|
+
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600 mt-1">
|
|
36
35
|
<input type="hidden" name="messages[][queue_name]" value="<%= m[:queue_name] %>" form="bulk-discard-dlq-form" disabled>
|
|
37
36
|
<input type="hidden" name="messages[][msg_id]" value="<%= m[:msg_id] %>" form="bulk-discard-dlq-form" disabled>
|
|
38
37
|
</td>
|
|
@@ -163,18 +163,18 @@
|
|
|
163
163
|
import { renderCharts, observeThemeChanges } from "charts";
|
|
164
164
|
|
|
165
165
|
const i18n = {
|
|
166
|
-
seriesName:
|
|
167
|
-
noData:
|
|
168
|
-
failedToLoad:
|
|
169
|
-
latencyAvg:
|
|
170
|
-
latencyP95:
|
|
166
|
+
seriesName: <%= t("pgbus.insights.show.charts.series_name").to_json.html_safe %>,
|
|
167
|
+
noData: <%= t("pgbus.insights.show.charts.no_data").to_json.html_safe %>,
|
|
168
|
+
failedToLoad: <%= t("pgbus.insights.show.charts.failed_to_load").to_json.html_safe %>,
|
|
169
|
+
latencyAvg: <%= t("pgbus.insights.show.charts.latency_avg").to_json.html_safe %>,
|
|
170
|
+
latencyP95: <%= t("pgbus.insights.show.charts.latency_p95").to_json.html_safe %>,
|
|
171
171
|
};
|
|
172
172
|
|
|
173
173
|
let chartData = null;
|
|
174
174
|
|
|
175
175
|
observeThemeChanges(() => chartData, i18n);
|
|
176
176
|
|
|
177
|
-
fetch(
|
|
177
|
+
fetch(<%= pgbus.api_insights_path(minutes: @minutes).to_json.html_safe %>)
|
|
178
178
|
.then(r => {
|
|
179
179
|
if (!r.ok) throw new Error("HTTP " + r.status);
|
|
180
180
|
return r.json();
|
|
@@ -45,11 +45,10 @@
|
|
|
45
45
|
<% payload = pgbus_parse_message(j[:message]) %>
|
|
46
46
|
<tr>
|
|
47
47
|
<td class="w-10 px-4 py-3 align-top">
|
|
48
|
-
<input type="checkbox" data-bulk-item
|
|
48
|
+
<input type="checkbox" data-bulk-item data-pgbus-action="bulk-row-toggle"
|
|
49
49
|
aria-label="<%= t("pgbus.helpers.bulk_select_row", id: j[:msg_id]) %>"
|
|
50
50
|
data-queue-name="<%= j[:queue_name] %>" data-msg-id="<%= j[:msg_id] %>"
|
|
51
|
-
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600 mt-1"
|
|
52
|
-
onchange="this.nextElementSibling.disabled = !this.checked; this.nextElementSibling.nextElementSibling.disabled = !this.checked">
|
|
51
|
+
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600 mt-1">
|
|
53
52
|
<input type="hidden" name="messages[][queue_name]" value="<%= j[:queue_name] %>" form="bulk-discard-enqueued-form" disabled>
|
|
54
53
|
<input type="hidden" name="messages[][msg_id]" value="<%= j[:msg_id] %>" form="bulk-discard-enqueued-form" disabled>
|
|
55
54
|
</td>
|
data/lib/pgbus/client.rb
CHANGED
|
@@ -221,6 +221,49 @@ module Pgbus
|
|
|
221
221
|
result
|
|
222
222
|
end
|
|
223
223
|
|
|
224
|
+
# Check whether a message exists in the given queue.
|
|
225
|
+
#
|
|
226
|
+
# Pass either +msg_id+ for a fast primary-key lookup, or +uniqueness_key+
|
|
227
|
+
# to scan the queue for any message whose payload carries that key in the
|
|
228
|
+
# +pgbus_uniqueness_key+ JSONB field. The latter is used by the dispatcher
|
|
229
|
+
# reaper to determine if a uniqueness lock with msg_id=0 (placeholder)
|
|
230
|
+
# still has a corresponding queue message.
|
|
231
|
+
#
|
|
232
|
+
# +queue_name+ may be either a logical name (e.g. "default") or an already
|
|
233
|
+
# prefixed physical name (e.g. "pgbus_default"). The client normalizes both.
|
|
234
|
+
#
|
|
235
|
+
# Returns:
|
|
236
|
+
# true — the message definitely exists in the queue
|
|
237
|
+
# false — the message definitely does not exist
|
|
238
|
+
# nil — could not determine (e.g. queue table missing or unknown error).
|
|
239
|
+
# Callers MUST treat nil as "exists" for safety.
|
|
240
|
+
def message_exists?(queue_name, msg_id: nil, uniqueness_key: nil)
|
|
241
|
+
has_msg_id = !msg_id.nil?
|
|
242
|
+
has_uniqueness_key = !uniqueness_key.nil?
|
|
243
|
+
raise ArgumentError, "pass exactly one of msg_id or uniqueness_key" unless has_msg_id ^ has_uniqueness_key
|
|
244
|
+
|
|
245
|
+
full_name = resolve_full_queue_name(queue_name)
|
|
246
|
+
sanitized = QueueNameValidator.sanitize!(full_name)
|
|
247
|
+
|
|
248
|
+
synchronized do
|
|
249
|
+
with_raw_connection do |conn|
|
|
250
|
+
if has_msg_id
|
|
251
|
+
msg_id_present?(conn, sanitized, msg_id.to_i)
|
|
252
|
+
else
|
|
253
|
+
uniqueness_key_present?(conn, sanitized, uniqueness_key)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
258
|
+
raise unless undefined_table_error?(e)
|
|
259
|
+
|
|
260
|
+
nil
|
|
261
|
+
rescue StandardError => e
|
|
262
|
+
raise unless defined?(PG::UndefinedTable) && e.is_a?(PG::UndefinedTable)
|
|
263
|
+
|
|
264
|
+
nil
|
|
265
|
+
end
|
|
266
|
+
|
|
224
267
|
def purge_archive(queue_name, older_than:, batch_size: 1000)
|
|
225
268
|
full_name = config.queue_name(queue_name)
|
|
226
269
|
sanitized = QueueNameValidator.sanitize!(full_name)
|
|
@@ -266,6 +309,42 @@ module Pgbus
|
|
|
266
309
|
|
|
267
310
|
private
|
|
268
311
|
|
|
312
|
+
# Accept either a logical name ("default") or an already-prefixed
|
|
313
|
+
# physical name ("pgbus_default") and return the physical name.
|
|
314
|
+
# Coerces symbols to strings so callers can pass either form.
|
|
315
|
+
def resolve_full_queue_name(queue_name)
|
|
316
|
+
name = queue_name.to_s
|
|
317
|
+
prefix = "#{config.queue_prefix}_"
|
|
318
|
+
name.start_with?(prefix) ? name : config.queue_name(name)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def msg_id_present?(conn, sanitized, msg_id)
|
|
322
|
+
result = conn.exec_params(
|
|
323
|
+
"SELECT 1 FROM pgmq.q_#{sanitized} WHERE msg_id = $1 LIMIT 1",
|
|
324
|
+
[msg_id]
|
|
325
|
+
)
|
|
326
|
+
result.ntuples.positive?
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def uniqueness_key_present?(conn, sanitized, uniqueness_key)
|
|
330
|
+
result = conn.exec_params(
|
|
331
|
+
"SELECT 1 FROM pgmq.q_#{sanitized} " \
|
|
332
|
+
"WHERE message::jsonb ->> 'pgbus_uniqueness_key' = $1 LIMIT 1",
|
|
333
|
+
[uniqueness_key]
|
|
334
|
+
)
|
|
335
|
+
result.ntuples.positive?
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Detect "relation does not exist" via the underlying PG error type.
|
|
339
|
+
# Falls back to message matching only if PG::UndefinedTable is undefined
|
|
340
|
+
# (very old pg gem) — never relies on locale-sensitive text.
|
|
341
|
+
def undefined_table_error?(error)
|
|
342
|
+
cause = error.respond_to?(:cause) ? error.cause : nil
|
|
343
|
+
return true if defined?(PG::UndefinedTable) && cause.is_a?(PG::UndefinedTable)
|
|
344
|
+
|
|
345
|
+
false
|
|
346
|
+
end
|
|
347
|
+
|
|
269
348
|
def collect_configured_queues
|
|
270
349
|
queues = Set.new
|
|
271
350
|
queues << config.default_queue
|
|
@@ -32,7 +32,14 @@ module Pgbus
|
|
|
32
32
|
]
|
|
33
33
|
)
|
|
34
34
|
rescue StandardError => e
|
|
35
|
-
|
|
35
|
+
# ERROR-level: silent loss of failure-tracking data defeats the
|
|
36
|
+
# purpose of the dashboard's "Failed Jobs" section. If recording
|
|
37
|
+
# fails, surface it loudly so the broken state can be diagnosed
|
|
38
|
+
# rather than silently masked.
|
|
39
|
+
Pgbus.logger.error do
|
|
40
|
+
"[Pgbus] Failed to record failed event for queue=#{queue_name} msg_id=#{msg_id}: " \
|
|
41
|
+
"#{e.class}: #{e.message}"
|
|
42
|
+
end
|
|
36
43
|
end
|
|
37
44
|
|
|
38
45
|
def clear!(queue_name:, msg_id:)
|
|
@@ -42,7 +49,13 @@ module Pgbus
|
|
|
42
49
|
[queue_name, msg_id.to_i]
|
|
43
50
|
)
|
|
44
51
|
rescue StandardError => e
|
|
45
|
-
|
|
52
|
+
# ERROR-level: a failed clear leaves a stale row in the dashboard
|
|
53
|
+
# AFTER the job actually succeeded — confusing and load-bearing
|
|
54
|
+
# for users debugging recurring duplicates.
|
|
55
|
+
Pgbus.logger.error do
|
|
56
|
+
"[Pgbus] Failed to clear failed event for queue=#{queue_name} msg_id=#{msg_id}: " \
|
|
57
|
+
"#{e.class}: #{e.message}"
|
|
58
|
+
end
|
|
46
59
|
end
|
|
47
60
|
|
|
48
61
|
private
|
|
@@ -144,9 +144,11 @@ module Pgbus
|
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
def cleanup_job_locks
|
|
147
|
-
# Clean up orphaned uniqueness keys whose
|
|
148
|
-
# in
|
|
149
|
-
#
|
|
147
|
+
# Clean up truly orphaned uniqueness keys: rows whose referenced
|
|
148
|
+
# message no longer exists in the PGMQ queue. This handles crashes
|
|
149
|
+
# or queue truncation. It must NEVER delete a lock while the message
|
|
150
|
+
# is still in the queue, even if the lock is "old" — recurring jobs
|
|
151
|
+
# that fail and retry can hold locks for hours.
|
|
150
152
|
reaped = reap_orphaned_uniqueness_keys
|
|
151
153
|
Pgbus.logger.info { "[Pgbus] Reaped #{reaped} orphaned uniqueness keys" } if reaped.positive?
|
|
152
154
|
end
|
|
@@ -155,20 +157,17 @@ module Pgbus
|
|
|
155
157
|
keys = UniquenessKey.all.to_a
|
|
156
158
|
return 0 if keys.empty?
|
|
157
159
|
|
|
160
|
+
# Only consider locks that are old enough that we wouldn't be racing
|
|
161
|
+
# an in-flight enqueue. visibility_timeout * 2 is the floor — anything
|
|
162
|
+
# younger could be a freshly-acquired lock whose send_message hasn't
|
|
163
|
+
# committed yet.
|
|
158
164
|
threshold = Time.current - (config.visibility_timeout * 2)
|
|
159
165
|
|
|
160
166
|
orphaned = keys.select do |key|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
next true if key.msg_id.zero? && key.created_at && key.created_at < threshold
|
|
166
|
-
|
|
167
|
-
# For real msg_ids, only reap if stale (old enough that VT has
|
|
168
|
-
# long expired). The message itself may still be in the queue
|
|
169
|
-
# awaiting retry — age is the only safe signal without scanning
|
|
170
|
-
# every queue table.
|
|
171
|
-
key.created_at && key.created_at < threshold
|
|
167
|
+
next false unless key.created_at && key.created_at < threshold
|
|
168
|
+
next false unless key.queue_name
|
|
169
|
+
|
|
170
|
+
message_gone?(key)
|
|
172
171
|
end
|
|
173
172
|
|
|
174
173
|
return 0 if orphaned.empty?
|
|
@@ -179,6 +178,27 @@ module Pgbus
|
|
|
179
178
|
0
|
|
180
179
|
end
|
|
181
180
|
|
|
181
|
+
# Returns true if the message referenced by this lock is definitely gone
|
|
182
|
+
# from the queue. Returns false otherwise (message present, or unknown).
|
|
183
|
+
#
|
|
184
|
+
# Routes through Pgbus::Client#message_exists? so all PGMQ access stays
|
|
185
|
+
# behind the client interface. The client returns nil when it can't
|
|
186
|
+
# determine the answer (queue table missing, etc.); we treat that as
|
|
187
|
+
# "still here" — the reaper must NEVER delete a lock when in doubt.
|
|
188
|
+
def message_gone?(key)
|
|
189
|
+
msg_id = key.msg_id.to_i
|
|
190
|
+
result = if msg_id.positive?
|
|
191
|
+
Pgbus.client.message_exists?(key.queue_name, msg_id: msg_id)
|
|
192
|
+
else
|
|
193
|
+
Pgbus.client.message_exists?(key.queue_name, uniqueness_key: key.lock_key)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
result == false
|
|
197
|
+
rescue StandardError => e
|
|
198
|
+
Pgbus.logger.warn { "[Pgbus] Reap check failed for #{key.lock_key}: #{e.message}" }
|
|
199
|
+
false
|
|
200
|
+
end
|
|
201
|
+
|
|
182
202
|
def cleanup_outbox
|
|
183
203
|
return unless config.outbox_enabled
|
|
184
204
|
|
data/lib/pgbus/process/worker.rb
CHANGED
|
@@ -133,7 +133,7 @@ module Pgbus
|
|
|
133
133
|
fetch_multi(active_queues, qty)
|
|
134
134
|
end
|
|
135
135
|
rescue StandardError => e
|
|
136
|
-
if
|
|
136
|
+
if undefined_queue_table_error?(e)
|
|
137
137
|
evict_missing_queues(e)
|
|
138
138
|
else
|
|
139
139
|
Pgbus.logger.error { "[Pgbus] Error fetching messages: #{e.message}" }
|
|
@@ -141,6 +141,24 @@ module Pgbus
|
|
|
141
141
|
[]
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
+
# Detect "queue table missing" via the underlying PG::UndefinedTable
|
|
145
|
+
# cause when available. Falls back to a guarded message check that
|
|
146
|
+
# requires BOTH "pgmq.q_" (so we know it's our queue table) and
|
|
147
|
+
# "does not exist", which keeps the eviction logic working for
|
|
148
|
+
# adapters/exception wrappers that don't preserve the original
|
|
149
|
+
# PG::UndefinedTable as #cause (e.g. PGMQ::Errors::ConnectionError
|
|
150
|
+
# raised by pgmq-ruby's auto-reconnect path). Locale-fragile, but
|
|
151
|
+
# this is gated by the very specific "pgmq.q_" prefix so a false
|
|
152
|
+
# positive can only come from another error mentioning that exact
|
|
153
|
+
# string — which is itself a queue-table error worth handling.
|
|
154
|
+
def undefined_queue_table_error?(error)
|
|
155
|
+
cause = error.respond_to?(:cause) ? error.cause : nil
|
|
156
|
+
return true if defined?(PG::UndefinedTable) && cause.is_a?(PG::UndefinedTable)
|
|
157
|
+
return true if error.message.include?("pgmq.q_") && error.message.include?("does not exist")
|
|
158
|
+
|
|
159
|
+
false
|
|
160
|
+
end
|
|
161
|
+
|
|
144
162
|
def fetch_prioritized(active_queues, qty)
|
|
145
163
|
remaining = qty
|
|
146
164
|
results = []
|
data/lib/pgbus/uniqueness.rb
CHANGED
|
@@ -81,11 +81,16 @@ module Pgbus
|
|
|
81
81
|
|
|
82
82
|
args = active_job.arguments
|
|
83
83
|
last = args.last
|
|
84
|
-
if last.is_a?(Hash) && last.each_key.all?(Symbol)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
key = if last.is_a?(Hash) && last.each_key.all?(Symbol)
|
|
85
|
+
config[:key].call(*args[...-1], **last)
|
|
86
|
+
else
|
|
87
|
+
config[:key].call(*args)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Automatically serialize GlobalID-compatible objects (e.g. ActiveRecord models)
|
|
91
|
+
# so users can pass model instances directly without manual .to_global_id.to_s
|
|
92
|
+
key = key.to_global_id.to_s if key.respond_to?(:to_global_id)
|
|
93
|
+
key
|
|
89
94
|
end
|
|
90
95
|
|
|
91
96
|
def inject_metadata(active_job, payload_hash)
|
data/lib/pgbus/version.rb
CHANGED
|
@@ -177,16 +177,24 @@ module Pgbus
|
|
|
177
177
|
event = failed_event(id)
|
|
178
178
|
return false unless event
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
# Prefer resetting the existing message's visibility timeout to 0
|
|
181
|
+
# so the worker picks it up immediately. This avoids creating a
|
|
182
|
+
# duplicate (the original is still in the queue waiting for retry).
|
|
183
|
+
# Falls back to enqueueing a fresh copy only if the original is gone
|
|
184
|
+
# (e.g., already moved to DLQ).
|
|
185
|
+
msg_id = event["msg_id"]
|
|
186
|
+
if msg_id && @client.message_exists?(event["queue_name"], msg_id: msg_id.to_i)
|
|
187
|
+
@client.set_visibility_timeout(event["queue_name"], msg_id.to_i, vt: 0)
|
|
188
|
+
else
|
|
189
|
+
payload = JSON.parse(event["payload"])
|
|
190
|
+
headers = event["headers"]
|
|
191
|
+
headers = JSON.parse(headers) if headers.is_a?(String)
|
|
185
192
|
@client.send_message(event["queue_name"], payload, headers: headers)
|
|
186
|
-
connection.exec_delete(
|
|
187
|
-
"DELETE FROM pgbus_failed_events WHERE id = $1", "Pgbus Delete Failed Event", [id.to_i]
|
|
188
|
-
)
|
|
189
193
|
end
|
|
194
|
+
|
|
195
|
+
connection.exec_delete(
|
|
196
|
+
"DELETE FROM pgbus_failed_events WHERE id = $1", "Pgbus Delete Failed Event", [id.to_i]
|
|
197
|
+
)
|
|
190
198
|
true
|
|
191
199
|
rescue StandardError => e
|
|
192
200
|
Pgbus.logger.debug { "[Pgbus::Web] Error retrying failed event #{id}: #{e.message}" }
|
|
@@ -195,7 +203,10 @@ module Pgbus
|
|
|
195
203
|
|
|
196
204
|
def discard_failed_event(id)
|
|
197
205
|
event = failed_event(id)
|
|
198
|
-
|
|
206
|
+
if event
|
|
207
|
+
release_lock_for_payload(event["payload"])
|
|
208
|
+
archive_failed_message(event)
|
|
209
|
+
end
|
|
199
210
|
|
|
200
211
|
connection.exec_delete(
|
|
201
212
|
"DELETE FROM pgbus_failed_events WHERE id = $1", "Pgbus Delete Failed Event", [id.to_i]
|
|
@@ -235,6 +246,7 @@ module Pgbus
|
|
|
235
246
|
|
|
236
247
|
def discard_all_failed
|
|
237
248
|
release_locks_for_failed_events
|
|
249
|
+
archive_all_failed_messages
|
|
238
250
|
|
|
239
251
|
result = connection.execute("DELETE FROM pgbus_failed_events")
|
|
240
252
|
result.cmd_tuples
|
|
@@ -849,6 +861,29 @@ module Pgbus
|
|
|
849
861
|
Pgbus.logger.debug { "[Pgbus::Web] Error releasing locks for failed events: #{e.message}" }
|
|
850
862
|
end
|
|
851
863
|
|
|
864
|
+
# Archive the queue message a failed_event row points to. Idempotent —
|
|
865
|
+
# silently no-ops if the message no longer exists in the queue.
|
|
866
|
+
def archive_failed_message(event)
|
|
867
|
+
return unless event["queue_name"] && event["msg_id"]
|
|
868
|
+
|
|
869
|
+
@client.archive_message(event["queue_name"], event["msg_id"].to_i)
|
|
870
|
+
rescue StandardError => e
|
|
871
|
+
Pgbus.logger.debug do
|
|
872
|
+
"[Pgbus::Web] Error archiving message for failed event #{event["id"]}: #{e.message}"
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
# Archive every queue message referenced by a failed_event row.
|
|
877
|
+
def archive_all_failed_messages
|
|
878
|
+
rows = connection.select_all(
|
|
879
|
+
"SELECT id, queue_name, msg_id FROM pgbus_failed_events WHERE msg_id IS NOT NULL",
|
|
880
|
+
"Pgbus Collect Failed Messages"
|
|
881
|
+
)
|
|
882
|
+
rows.to_a.each { |row| archive_failed_message(row) }
|
|
883
|
+
rescue StandardError => e
|
|
884
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error archiving failed messages: #{e.message}" }
|
|
885
|
+
end
|
|
886
|
+
|
|
852
887
|
# Release all uniqueness keys associated with a queue before purge/drop.
|
|
853
888
|
# Scans queue messages for uniqueness metadata and deletes matching rows.
|
|
854
889
|
def release_uniqueness_keys_for_queue(queue_name)
|