rails_event_viewer 0.2.1 → 0.3.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/CHANGELOG.md +48 -0
- data/README.md +24 -1
- data/Rakefile +2 -0
- data/app/assets/javascripts/rails_event_viewer/application.js +2 -1
- data/app/assets/stylesheets/rails_event_viewer/application.css +1 -1
- data/app/controllers/rails_event_viewer/analytics_controller.rb +5 -30
- data/app/controllers/rails_event_viewer/application_controller.rb +11 -5
- data/app/controllers/rails_event_viewer/dashboard_controller.rb +1 -1
- data/app/controllers/rails_event_viewer/event_types_controller.rb +3 -1
- data/app/controllers/rails_event_viewer/events_controller.rb +10 -23
- data/app/controllers/rails_event_viewer/groups_controller.rb +4 -11
- data/app/helpers/rails_event_viewer/application_helper.rb +2 -2
- data/app/views/layouts/rails_event_viewer/application.html.erb +8 -0
- data/app/views/rails_event_viewer/analytics/overview.html.erb +2 -0
- data/app/views/rails_event_viewer/dashboard/index.html.erb +4 -3
- data/app/views/rails_event_viewer/event_types/index.html.erb +1 -1
- data/app/views/rails_event_viewer/event_types/show.html.erb +5 -3
- data/app/views/rails_event_viewer/events/_event_row.html.erb +3 -3
- data/app/views/rails_event_viewer/events/_filters_sidebar.html.erb +2 -1
- data/app/views/rails_event_viewer/events/index.html.erb +4 -4
- data/app/views/rails_event_viewer/events/show.html.erb +7 -7
- data/app/views/rails_event_viewer/groups/index.html.erb +2 -2
- data/app/views/rails_event_viewer/groups/show.html.erb +1 -5
- data/app/views/rails_event_viewer/shared/_navigation.html.erb +7 -5
- data/config/routes.rb +6 -9
- data/db/migrate/{20241208000001_create_rails_event_viewer_entries.rb → 20261002121337_create_rails_event_viewer_entries.rb} +9 -4
- data/lib/generators/rails_event_viewer/install/templates/create_rails_event_viewer_entries.rb.erb +8 -3
- data/lib/generators/rails_event_viewer/install/templates/initializer.rb.erb +7 -9
- data/lib/rails_event_viewer/adapter.rb +2 -2
- data/lib/rails_event_viewer/adapters/active_record.rb +15 -36
- data/lib/rails_event_viewer/adapters/concerns/in_memory_filtering.rb +29 -3
- data/lib/rails_event_viewer/adapters/memory.rb +1 -21
- data/lib/rails_event_viewer/adapters/null.rb +2 -2
- data/lib/rails_event_viewer/adapters/redis.rb +4 -28
- data/lib/rails_event_viewer/batch_writer.rb +2 -1
- data/lib/rails_event_viewer/events_relation.rb +1 -1
- data/lib/rails_event_viewer/json_query.rb +52 -60
- data/lib/rails_event_viewer/subscriber.rb +1 -2
- data/lib/rails_event_viewer/time_utils.rb +3 -10
- data/lib/rails_event_viewer/version.rb +1 -1
- data/lib/tasks/rails_event_viewer_tasks.rake +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 876a5c4676cfb1f5512d82e73464c1e69a19bd9b1a1b5c2854f93aef7c412a60
|
|
4
|
+
data.tar.gz: 766e263c994dc9552472e065e1abaea18064cc08858c45ef524148923693922f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b3b9d72c97349b344106c874be9988e605c05efda5cafdb24e8230603830d313cb3a1b2fb72722a59ce6a27dab43e08751abebcd39606fd23ff2337c84392eac
|
|
7
|
+
data.tar.gz: 4e0cbc54525cdaa6b177832bd53e7207d1f3fc4c0a6167c3da250e85b00bea27bdbf1073e7a555b65887514bcc6e4e1ccdfe18b53c8a6fd1ffd49b74c82511a0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.1 (2026-10-03)
|
|
4
|
+
|
|
5
|
+
Changed:
|
|
6
|
+
|
|
7
|
+
- The nav search goes to the events list and works together with the filters. The `/events/search` page is removed.
|
|
8
|
+
- The Analytics link is hidden when the adapter has no analytics.
|
|
9
|
+
- The group page no longer shows an Event Types count. It counted only the current page.
|
|
10
|
+
|
|
11
|
+
Fixed:
|
|
12
|
+
|
|
13
|
+
- Array, hash and invalid values in query params are ignored instead of raising.
|
|
14
|
+
- Event rows contain a real link, so keyboard and new-tab clicks work. Selecting text in a row no longer opens the event.
|
|
15
|
+
- `rails_event_viewer:clear` works with the ActiveRecord adapter.
|
|
16
|
+
- Redis keeps event times to the microsecond, and `delete_before` keeps an event at the exact cutoff.
|
|
17
|
+
- Payloads show `&`, `<` and `>` instead of unicode escapes.
|
|
18
|
+
- A banner explains a missing events table. A filtered empty list says no events match.
|
|
19
|
+
- Pages fit narrow screens, and long names and payloads wrap.
|
|
20
|
+
- Charts render when the host app sets its own Chartkick `content_for`.
|
|
21
|
+
- `with_name` accepts a symbol.
|
|
22
|
+
|
|
23
|
+
## 0.3.0 (2026-10-02)
|
|
24
|
+
|
|
25
|
+
Breaking:
|
|
26
|
+
|
|
27
|
+
- Event type and group pages moved to `/event_type?name=...` and `/group?key=...&value=...`. Old links to those pages no longer work.
|
|
28
|
+
- Custom adapters: `counts_by_name` and `events_over_time` now take a `range:` argument.
|
|
29
|
+
- Unknown options for the Redis and memory adapters raise an `ArgumentError`.
|
|
30
|
+
- `with_tag` and `with_context` with a value of `""` or `false` now match that value, not every event that has the key.
|
|
31
|
+
|
|
32
|
+
Fixed:
|
|
33
|
+
|
|
34
|
+
- PostgreSQL: group pages no longer raise.
|
|
35
|
+
- MySQL: the migration runs and search works.
|
|
36
|
+
- Event names and group values with a slash no longer break the Event Types and Groups pages.
|
|
37
|
+
- Tag and context filters return the same events on every database and adapter. A value typed as text also matches numbers and booleans.
|
|
38
|
+
- Filter keys with a hyphen, dot, space or quote work on every database.
|
|
39
|
+
- Events the database rejects no longer hold up the events behind them.
|
|
40
|
+
- Payloads that cannot be serialized, such as invalid UTF-8, are stored as text.
|
|
41
|
+
- A dropped connection during the first table check no longer stops capture until restart.
|
|
42
|
+
- The analytics page respects its date range.
|
|
43
|
+
- The generated Redis config suggests `redis_options`, which the adapter reads.
|
|
44
|
+
- The generated initializer adds to `ignored_events` and keeps the defaults.
|
|
45
|
+
|
|
46
|
+
Changed:
|
|
47
|
+
|
|
48
|
+
- Removed two unused analytics endpoints.
|
|
49
|
+
- The engine migration has a new timestamp and targets Rails 8.1. Apps that already installed it need no action.
|
|
50
|
+
|
|
3
51
|
## 0.2.1 (2026-10-01)
|
|
4
52
|
|
|
5
53
|
- Forked workers no longer duplicate the parent's buffered events on exit.
|
data/README.md
CHANGED
|
@@ -43,6 +43,7 @@ RailsEventViewer.events
|
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
Other filters: `with_context`, `search`, `until`, `offset`, and `count`.
|
|
46
|
+
Without `limit`, a query returns `config.per_page` events, 25 by default.
|
|
46
47
|
|
|
47
48
|
## Configuration
|
|
48
49
|
|
|
@@ -90,10 +91,23 @@ config.authentication = ->(controller) {
|
|
|
90
91
|
|
|
91
92
|
Set `RAILS_ENV` on every server. Without it, Rails falls back to development and the dashboard is open.
|
|
92
93
|
|
|
94
|
+
If both are set, HTTP Basic Auth is used and `config.authentication` is ignored.
|
|
95
|
+
|
|
93
96
|
## Storage adapters
|
|
94
97
|
|
|
95
98
|
Set `config.storage_adapter` to `:active_record` (default), `:redis`, `:memory`, or `:null`. Redis needs the `redis` gem and takes options through `config.adapter_options`.
|
|
96
99
|
|
|
100
|
+
```ruby
|
|
101
|
+
config.storage_adapter = :redis
|
|
102
|
+
config.adapter_options = {
|
|
103
|
+
redis_options: { url: ENV["REDIS_URL"] }, # passed to Redis.new
|
|
104
|
+
key_prefix: "rails_event_viewer",
|
|
105
|
+
max_events: 10_000
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Pass `pool:` to use your own `ConnectionPool`, or `pool_size:` and `pool_timeout:` to size the built-in one. The memory adapter takes `max_events:`. An unknown option raises an `ArgumentError`.
|
|
110
|
+
|
|
97
111
|
Filtered queries on Redis scan every stored event, so keep `max_events` (how many events Redis keeps, set in `adapter_options`) modest. For your own storage, include `RailsEventViewer::Adapter`.
|
|
98
112
|
|
|
99
113
|
## Rake tasks
|
|
@@ -119,7 +133,16 @@ production:
|
|
|
119
133
|
```bash
|
|
120
134
|
bundle install
|
|
121
135
|
bundle exec rake test
|
|
122
|
-
|
|
136
|
+
bin/rails db:migrate && bin/rails server
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`rake test` prepares the test database first. Run it once before running a single test file.
|
|
140
|
+
|
|
141
|
+
To test against PostgreSQL or MySQL, set `DATABASE_URL`:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
DATABASE_URL=postgresql://localhost/rails_event_viewer_test bundle exec rake test
|
|
145
|
+
DATABASE_URL=trilogy://root@127.0.0.1/rails_event_viewer_test bundle exec rake test
|
|
123
146
|
```
|
|
124
147
|
|
|
125
148
|
## License
|
data/Rakefile
CHANGED
|
@@ -22,7 +22,8 @@ document.addEventListener("click", (event) => {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const row = event.target.closest("[data-event-viewer-href]")
|
|
25
|
-
|
|
25
|
+
const selectingText = window.getSelection().toString() !== ""
|
|
26
|
+
if (row && !selectingText && !event.target.closest("a, button")) {
|
|
26
27
|
window.location = row.dataset.eventViewerHref
|
|
27
28
|
}
|
|
28
29
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.inset-y-0{top:0;bottom:0}.bottom-0{bottom:0}.left-0{left:0}.left-10{left:2.5rem}.left-12{left:3rem}.top-0{top:0}.top-4{top:1rem}.top-6{top:1.5rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-10{margin-left:2.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-5{margin-left:1.25rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-72{height:18rem}.h-full{height:100%}.min-h-full{min-height:100%}.w-0{width:0}.w-0\.5{width:.125rem}.w-1\/5{width:20%}.w-12{width:3rem}.w-2\/5{width:40%}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-72{width:18rem}.w-9{width:2.25rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.min-w-0{min-width:0}.min-w-full{min-width:100%}.max-w-7xl{max-width:80rem}.max-w-md{max-width:28rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.table-fixed{table-layout:fixed}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity,1))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.border-indigo-600{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity,1))}.border-yellow-200{--tw-border-opacity:1;border-color:rgb(254 240 138/var(--tw-border-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity,1))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity,1))}.bg-indigo-700{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity,1))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity,1))}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pl-10{padding-left:2.5rem}.pl-16{padding-left:4rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pr-6{padding-right:1.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tracking-wider{letter-spacing:.05em}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity,1))}.text-indigo-200{--tw-text-opacity:1;color:rgb(199 210 254/var(--tw-text-opacity,1))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.text-indigo-800{--tw-text-opacity:1;color:rgb(55 48 163/var(--tw-text-opacity,1))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.text-yellow-700{--tw-text-opacity:1;color:rgb(161 98 7/var(--tw-text-opacity,1))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity,1))}.placeholder-indigo-200::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(199 210 254/var(--tw-placeholder-opacity,1))}.placeholder-indigo-200::placeholder{--tw-placeholder-opacity:1;color:rgb(199 210 254/var(--tw-placeholder-opacity,1))}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.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)}.json-tree{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.875rem}.json-tree-key{color:#4b5563;font-weight:500}.json-tree-string{color:#059669}.json-tree-number{color:#2563eb}.json-tree-boolean{color:#7c3aed}.json-tree-null{color:#9ca3af}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.hover\:bg-indigo-700:hover{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.hover\:text-indigo-500:hover{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity,1))}.hover\:text-indigo-900:hover{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.focus\:border-indigo-500:focus{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.focus\:bg-white:focus{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.focus\:text-gray-900:focus{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.focus\:placeholder-gray-400:focus::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.focus\:placeholder-gray-400:focus::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity,1))}.focus\:ring-white:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity,1))}@media (min-width:640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.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-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}}
|
|
1
|
+
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.inset-y-0{top:0;bottom:0}.bottom-0{bottom:0}.left-0{left:0}.left-10{left:2.5rem}.left-12{left:3rem}.top-0{top:0}.top-4{top:1rem}.top-6{top:1.5rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-10{margin-left:2.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-5{margin-left:1.25rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.h-12{height:3rem}.h-2{height:.5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-72{height:18rem}.h-full{height:100%}.min-h-16{min-height:4rem}.min-h-full{min-height:100%}.w-0{width:0}.w-0\.5{width:.125rem}.w-1\/5{width:20%}.w-12{width:3rem}.w-2\/5{width:40%}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-9{width:2.25rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.min-w-0{min-width:0}.min-w-full{min-width:100%}.max-w-7xl{max-width:80rem}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.table-fixed{table-layout:fixed}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-y-2{row-gap:.5rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity,1))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.border-indigo-600{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity,1))}.border-yellow-200{--tw-border-opacity:1;border-color:rgb(254 240 138/var(--tw-border-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity,1))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity,1))}.bg-indigo-700{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity,1))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity,1))}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pl-10{padding-left:2.5rem}.pl-16{padding-left:4rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pr-6{padding-right:1.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tracking-wider{letter-spacing:.05em}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity,1))}.text-indigo-200{--tw-text-opacity:1;color:rgb(199 210 254/var(--tw-text-opacity,1))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.text-indigo-800{--tw-text-opacity:1;color:rgb(55 48 163/var(--tw-text-opacity,1))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.text-yellow-700{--tw-text-opacity:1;color:rgb(161 98 7/var(--tw-text-opacity,1))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity,1))}.placeholder-indigo-200::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(199 210 254/var(--tw-placeholder-opacity,1))}.placeholder-indigo-200::placeholder{--tw-placeholder-opacity:1;color:rgb(199 210 254/var(--tw-placeholder-opacity,1))}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.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)}.json-tree{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.875rem}.json-tree-key{color:#4b5563;font-weight:500}.json-tree-string{color:#059669}.json-tree-number{color:#2563eb}.json-tree-boolean{color:#7c3aed}.json-tree-null{color:#9ca3af}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.hover\:bg-indigo-700:hover{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.hover\:text-indigo-500:hover{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity,1))}.hover\:text-indigo-900:hover{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.focus\:border-indigo-500:focus{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.focus\:bg-white:focus{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.focus\:text-gray-900:focus{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.focus\:placeholder-gray-400:focus::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.focus\:placeholder-gray-400:focus::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity,1))}.focus\:ring-white:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity,1))}@media (min-width:640px){.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\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:w-72{width:18rem}.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-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:px-8{padding-left:2rem;padding-right:2rem}}
|
|
@@ -4,42 +4,17 @@ module RailsEventViewer
|
|
|
4
4
|
|
|
5
5
|
def overview
|
|
6
6
|
@date_range = parse_date_range
|
|
7
|
+
counts = current_adapter.counts_by_name(range: @date_range)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@total_events = relation.count
|
|
13
|
-
@unique_event_types = current_adapter.distinct_event_names.size
|
|
14
|
-
@events_by_type = current_adapter.counts_by_name(limit: 20)
|
|
15
|
-
@events_over_time = current_adapter.events_over_time(since: @date_range.begin, interval: period_for_range)
|
|
9
|
+
@total_events = counts.values.sum
|
|
10
|
+
@unique_event_types = counts.size
|
|
11
|
+
@events_by_type = counts.first(20).to_h
|
|
12
|
+
@events_over_time = current_adapter.events_over_time(range: @date_range, interval: period_for_range)
|
|
16
13
|
|
|
17
14
|
@avg_events_per_hour = calculate_avg_events_per_hour
|
|
18
15
|
@peak_period = find_peak_period
|
|
19
16
|
end
|
|
20
17
|
|
|
21
|
-
def events_over_time
|
|
22
|
-
since = params[:since]&.to_i&.seconds&.ago || 24.hours.ago
|
|
23
|
-
interval = params[:interval]&.to_sym || :hour
|
|
24
|
-
|
|
25
|
-
@events_data = current_adapter.events_over_time(since: since, interval: interval)
|
|
26
|
-
|
|
27
|
-
respond_to do |format|
|
|
28
|
-
format.json { render json: @events_data }
|
|
29
|
-
format.html { render partial: "events_over_time_chart" }
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def events_by_type
|
|
34
|
-
limit = (params[:limit] || 20).to_i.clamp(1, 100)
|
|
35
|
-
@data = current_adapter.counts_by_name(limit: limit)
|
|
36
|
-
|
|
37
|
-
respond_to do |format|
|
|
38
|
-
format.json { render json: @data }
|
|
39
|
-
format.html { render partial: "events_by_type_chart" }
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
18
|
private
|
|
44
19
|
|
|
45
20
|
def check_analytics_support
|
|
@@ -42,11 +42,17 @@ module RailsEventViewer
|
|
|
42
42
|
Time.use_zone(zone, &block)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def string_param(key)
|
|
46
|
+
value = params[key]
|
|
47
|
+
value if value.is_a?(String) && !value.empty?
|
|
48
|
+
end
|
|
49
|
+
|
|
45
50
|
def safe_parse_date(date_string, default: nil)
|
|
46
|
-
return default
|
|
51
|
+
return default unless date_string.is_a?(String) && date_string.present?
|
|
47
52
|
|
|
48
|
-
Date.parse(date_string)
|
|
49
|
-
|
|
53
|
+
date = Date.parse(date_string)
|
|
54
|
+
date.year.between?(1000, 9999) ? date : default
|
|
55
|
+
rescue ArgumentError
|
|
50
56
|
default
|
|
51
57
|
end
|
|
52
58
|
|
|
@@ -67,11 +73,11 @@ module RailsEventViewer
|
|
|
67
73
|
end
|
|
68
74
|
|
|
69
75
|
def per_page
|
|
70
|
-
(
|
|
76
|
+
(string_param(:per_page) || RailsEventViewer.per_page).to_i.clamp(1, 100)
|
|
71
77
|
end
|
|
72
78
|
|
|
73
79
|
def paginate(relation)
|
|
74
|
-
pagination = Page.new(count: relation.count, per_page: per_page, page:
|
|
80
|
+
pagination = Page.new(count: relation.count, per_page: per_page, page: string_param(:page))
|
|
75
81
|
[pagination, relation.offset(pagination.offset).limit(pagination.per_page).to_a]
|
|
76
82
|
end
|
|
77
83
|
end
|
|
@@ -8,7 +8,7 @@ module RailsEventViewer
|
|
|
8
8
|
|
|
9
9
|
if analytics_supported?
|
|
10
10
|
@events_by_type = current_adapter.counts_by_name(limit: 10)
|
|
11
|
-
@events_over_time = current_adapter.events_over_time(
|
|
11
|
+
@events_over_time = current_adapter.events_over_time(range: 24.hours.ago.., interval: :hour)
|
|
12
12
|
else
|
|
13
13
|
@events_by_type = {}
|
|
14
14
|
@events_over_time = {}
|
|
@@ -5,7 +5,9 @@ module RailsEventViewer
|
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def show
|
|
8
|
-
@event_type_name =
|
|
8
|
+
@event_type_name = string_param(:name)
|
|
9
|
+
return redirect_to event_types_path unless @event_type_name
|
|
10
|
+
|
|
9
11
|
total_events = RailsEventViewer.events.with_name(@event_type_name)
|
|
10
12
|
|
|
11
13
|
@pagination, @events = paginate(total_events)
|
|
@@ -2,6 +2,7 @@ module RailsEventViewer
|
|
|
2
2
|
class EventsController < ApplicationController
|
|
3
3
|
def index
|
|
4
4
|
relation = apply_filters(RailsEventViewer.events)
|
|
5
|
+
@filtered = relation.filtered?
|
|
5
6
|
@pagination, @events = paginate(relation)
|
|
6
7
|
@event_names = current_adapter.distinct_event_names
|
|
7
8
|
end
|
|
@@ -17,37 +18,23 @@ module RailsEventViewer
|
|
|
17
18
|
@related_events = find_related_events(@event)
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
def search
|
|
21
|
-
@query = params[:q]
|
|
22
|
-
relation = RailsEventViewer.events.search(@query)
|
|
23
|
-
@pagination, @events = paginate(relation)
|
|
24
|
-
@event_names = current_adapter.distinct_event_names
|
|
25
|
-
|
|
26
|
-
render :index
|
|
27
|
-
end
|
|
28
|
-
|
|
29
21
|
private
|
|
30
22
|
|
|
31
23
|
def apply_filters(relation)
|
|
32
|
-
|
|
24
|
+
name = string_param(:name)
|
|
25
|
+
tag_key = string_param(:tag_key)
|
|
26
|
+
context_key = string_param(:context_key)
|
|
27
|
+
@query = string_param(:q)
|
|
33
28
|
|
|
34
29
|
@start_date = safe_parse_date(params[:start_date])
|
|
35
30
|
@end_date = safe_parse_date(params[:end_date])
|
|
31
|
+
|
|
32
|
+
relation = relation.with_name(name) if name
|
|
36
33
|
relation = relation.since(@start_date.beginning_of_day) if @start_date
|
|
37
34
|
relation = relation.until(@end_date.end_of_day) if @end_date
|
|
38
|
-
|
|
39
|
-
if
|
|
40
|
-
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
if params[:context_key].present?
|
|
44
|
-
relation = relation.with_context(params[:context_key], params[:context_value].presence)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
if params[:q].present?
|
|
48
|
-
relation = relation.search(params[:q])
|
|
49
|
-
end
|
|
50
|
-
|
|
35
|
+
relation = relation.with_tag(tag_key, string_param(:tag_value)) if tag_key
|
|
36
|
+
relation = relation.with_context(context_key, string_param(:context_value)) if context_key
|
|
37
|
+
relation = relation.search(@query) if @query
|
|
51
38
|
relation
|
|
52
39
|
end
|
|
53
40
|
|
|
@@ -17,8 +17,10 @@ module RailsEventViewer
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def show
|
|
20
|
-
@key =
|
|
21
|
-
@value =
|
|
20
|
+
@key = string_param(:key)
|
|
21
|
+
@value = string_param(:value)
|
|
22
|
+
return redirect_to groups_path unless @key && @value
|
|
23
|
+
|
|
22
24
|
@source = source_param
|
|
23
25
|
|
|
24
26
|
events = if @source == :tags
|
|
@@ -29,7 +31,6 @@ module RailsEventViewer
|
|
|
29
31
|
|
|
30
32
|
@pagination, @events = paginate(events)
|
|
31
33
|
@total_count = @pagination.count
|
|
32
|
-
@event_names = @events.map { |e| event_name(e) }.uniq
|
|
33
34
|
|
|
34
35
|
@first_event_at, @last_event_at = current_adapter.event_time_span(events)
|
|
35
36
|
@duration = @last_event_at - @first_event_at if @first_event_at && @last_event_at
|
|
@@ -40,13 +41,5 @@ module RailsEventViewer
|
|
|
40
41
|
def source_param
|
|
41
42
|
params[:source].presence_in(%w[context tags])&.to_sym || :context
|
|
42
43
|
end
|
|
43
|
-
|
|
44
|
-
def event_name(event)
|
|
45
|
-
event.respond_to?(:name) ? event.name : event[:name]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def event_occurred_at(event)
|
|
49
|
-
event.respond_to?(:occurred_at) ? event.occurred_at : event[:occurred_at]
|
|
50
|
-
end
|
|
51
44
|
end
|
|
52
45
|
end
|
|
@@ -25,13 +25,13 @@ module RailsEventViewer
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def truncate_json(json, length: 100)
|
|
28
|
-
text = json.is_a?(Hash) ? json
|
|
28
|
+
text = json.is_a?(Hash) ? JSON.generate(json) : json.to_s
|
|
29
29
|
truncate(text, length: length)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def json_tree(data, depth: 0, max_depth: nil)
|
|
33
33
|
return content_tag(:span, "null", class: "text-gray-400") if data.nil?
|
|
34
|
-
return content_tag(:span, data
|
|
34
|
+
return content_tag(:span, JSON.generate(data), class: json_value_class(data)) unless data.is_a?(Hash) || data.is_a?(Array)
|
|
35
35
|
return content_tag(:span, collapsed_summary(data), class: "text-gray-400") if max_depth && depth >= max_depth
|
|
36
36
|
|
|
37
37
|
if data.is_a?(Hash)
|
|
@@ -33,6 +33,14 @@
|
|
|
33
33
|
</div>
|
|
34
34
|
<% end %>
|
|
35
35
|
|
|
36
|
+
<% if RailsEventViewer.storage_adapter == :active_record && !persistence_supported? %>
|
|
37
|
+
<div class="mb-4 rounded-md bg-red-50 p-4" role="alert">
|
|
38
|
+
<p class="text-sm font-medium text-red-800">
|
|
39
|
+
The events table is missing, so no events are being saved. Run <code>bin/rails generate rails_event_viewer:install</code> and <code>bin/rails db:migrate</code>, then restart the app.
|
|
40
|
+
</p>
|
|
41
|
+
</div>
|
|
42
|
+
<% end %>
|
|
43
|
+
|
|
36
44
|
<% if RailsEventViewer.sample_rate.to_f < 1.0 %>
|
|
37
45
|
<div class="mb-4 rounded-md bg-yellow-50 border border-yellow-200 p-4">
|
|
38
46
|
<p class="text-sm font-medium text-yellow-800">
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
<%= line_chart @events_over_time,
|
|
48
48
|
id: "analytics-timeline",
|
|
49
49
|
html: '<div id="%{id}" class="h-full w-full">%{loading}</div>',
|
|
50
|
+
content_for: nil,
|
|
50
51
|
colors: ["#4F46E5"],
|
|
51
52
|
library: {
|
|
52
53
|
plugins: { legend: { display: false } },
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
<%= bar_chart @events_by_type,
|
|
68
69
|
id: "analytics-by-type",
|
|
69
70
|
html: '<div id="%{id}" class="h-full w-full">%{loading}</div>',
|
|
71
|
+
content_for: nil,
|
|
70
72
|
colors: ["#4F46E5"],
|
|
71
73
|
library: {
|
|
72
74
|
indexAxis: 'y',
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
<%= line_chart @events_over_time,
|
|
35
35
|
id: "events-timeline",
|
|
36
36
|
html: '<div id="%{id}" class="h-full w-full">%{loading}</div>',
|
|
37
|
+
content_for: nil,
|
|
37
38
|
colors: ["#4F46E5"],
|
|
38
39
|
library: { plugins: { legend: { display: false } } } %>
|
|
39
40
|
<% else %>
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
<%= pie_chart @events_by_type,
|
|
53
54
|
id: "events-by-type",
|
|
54
55
|
html: '<div id="%{id}" class="h-full w-full">%{loading}</div>',
|
|
56
|
+
content_for: nil,
|
|
55
57
|
donut: true,
|
|
56
58
|
colors: ["#4F46E5", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6", "#EC4899", "#06B6D4", "#84CC16"] %>
|
|
57
59
|
<% else %>
|
|
@@ -85,9 +87,8 @@
|
|
|
85
87
|
<% @recent_events.each do |event| %>
|
|
86
88
|
<tr class="hover:bg-gray-50 cursor-pointer" data-event-viewer-href="<%= rails_event_viewer.event_path(event_id(event)) %>">
|
|
87
89
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
</span>
|
|
90
|
+
<%= link_to event_name(event), rails_event_viewer.event_path(event_id(event)),
|
|
91
|
+
class: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800" %>
|
|
91
92
|
</td>
|
|
92
93
|
<td class="px-6 py-4 text-sm text-gray-500">
|
|
93
94
|
<code class="text-xs bg-gray-100 px-2 py-1 rounded"><%= truncate_json(event_payload(event), length: 60) %></code>
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
10
10
|
</svg>
|
|
11
11
|
<% end %>
|
|
12
|
-
<div>
|
|
12
|
+
<div class="min-w-0">
|
|
13
13
|
<h1 class="text-2xl font-bold text-gray-900">
|
|
14
|
-
<span class="inline-
|
|
14
|
+
<span class="inline-block max-w-full break-words px-3 py-1 rounded-full text-xl font-medium bg-indigo-100 text-indigo-800">
|
|
15
15
|
<%= @event_type_name %>
|
|
16
16
|
</span>
|
|
17
17
|
</h1>
|
|
@@ -56,7 +56,9 @@
|
|
|
56
56
|
<% @events.each do |event| %>
|
|
57
57
|
<tr class="hover:bg-gray-50 cursor-pointer" data-event-viewer-href="<%= rails_event_viewer.event_path(event_id(event)) %>">
|
|
58
58
|
<td class="px-6 py-4">
|
|
59
|
-
|
|
59
|
+
<%= link_to rails_event_viewer.event_path(event_id(event)) do %>
|
|
60
|
+
<code class="text-xs bg-gray-100 px-2 py-1 rounded"><%= truncate_json(event_payload(event), length: 80) %></code>
|
|
61
|
+
<% end %>
|
|
60
62
|
</td>
|
|
61
63
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
62
64
|
<% tags = event_tags(event) %>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<tr class="hover:bg-gray-50 cursor-pointer" data-event-viewer-href="<%= rails_event_viewer.event_path(event_id(event)) %>">
|
|
2
2
|
<td class="px-6 py-4">
|
|
3
3
|
<div class="flex flex-col max-w-md">
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
<%= link_to event_name(event), rails_event_viewer.event_path(event_id(event)),
|
|
5
|
+
title: event_name(event),
|
|
6
|
+
class: "inline-block max-w-full truncate px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800 w-fit" %>
|
|
7
7
|
<div class="text-sm text-gray-500 mt-1 truncate">
|
|
8
8
|
<code class="text-xs"><%= truncate_json(event_payload(event), length: 50) %></code>
|
|
9
9
|
</div>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<div class="bg-white rounded-lg shadow p-5 space-y-5">
|
|
2
2
|
<h3 class="font-medium text-gray-900">Filters</h3>
|
|
3
3
|
|
|
4
|
-
<%= form_with url: rails_event_viewer.events_path, method: :get,
|
|
4
|
+
<%= form_with url: rails_event_viewer.events_path, method: :get, class: "space-y-5" do |f| %>
|
|
5
|
+
<%= f.hidden_field :q, value: @query if @query %>
|
|
5
6
|
|
|
6
7
|
<!-- Event name filter -->
|
|
7
8
|
<div>
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
<% end %>
|
|
12
12
|
</h1>
|
|
13
13
|
<div class="text-sm text-gray-500">
|
|
14
|
-
<%= @pagination.count %>
|
|
14
|
+
<%= @pagination.count %> events
|
|
15
15
|
</div>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
-
<div class="flex gap-6">
|
|
18
|
+
<div class="flex flex-col lg:flex-row gap-6">
|
|
19
19
|
<!-- Filters sidebar -->
|
|
20
|
-
<aside class="w-72 flex-shrink-0">
|
|
20
|
+
<aside class="w-full lg:w-72 flex-shrink-0">
|
|
21
21
|
<%= render "rails_event_viewer/events/filters_sidebar", event_names: @event_names %>
|
|
22
22
|
</aside>
|
|
23
23
|
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
</svg>
|
|
52
52
|
<h3 class="mt-2 text-sm font-medium text-gray-900">No events found</h3>
|
|
53
53
|
<p class="mt-1 text-sm text-gray-500">
|
|
54
|
-
<% if @
|
|
54
|
+
<% if @filtered %>
|
|
55
55
|
Try adjusting your filters or search query.
|
|
56
56
|
<% else %>
|
|
57
57
|
Events will appear here once they are captured.
|
|
@@ -8,21 +8,21 @@
|
|
|
8
8
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
9
9
|
</svg>
|
|
10
10
|
<% end %>
|
|
11
|
-
<div>
|
|
12
|
-
<h1 class="text-2xl font-bold text-gray-900"><%= event_name(@event) %></h1>
|
|
11
|
+
<div class="min-w-0">
|
|
12
|
+
<h1 class="text-2xl font-bold text-gray-900 break-words"><%= event_name(@event) %></h1>
|
|
13
13
|
<p class="text-sm text-gray-500">Event ID: <%= event_id(@event) %></p>
|
|
14
14
|
</div>
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
17
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
18
18
|
<!-- Main content -->
|
|
19
|
-
<div class="lg:col-span-2 space-y-6">
|
|
19
|
+
<div class="lg:col-span-2 space-y-6 min-w-0">
|
|
20
20
|
<!-- Payload -->
|
|
21
21
|
<div class="bg-white rounded-lg shadow">
|
|
22
22
|
<div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
|
|
23
23
|
<h3 class="text-lg font-medium text-gray-900">Payload</h3>
|
|
24
24
|
<button type="button"
|
|
25
|
-
data-event-viewer-copy="<%= event_payload(@event)
|
|
25
|
+
data-event-viewer-copy="<%= JSON.generate(event_payload(@event)) %>"
|
|
26
26
|
class="text-indigo-600 hover:text-indigo-500 text-sm font-medium">
|
|
27
27
|
Copy
|
|
28
28
|
</button>
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<div class="p-6">
|
|
31
31
|
<% payload = event_payload(@event) %>
|
|
32
32
|
<% if payload.present? %>
|
|
33
|
-
<div class="font-mono text-sm">
|
|
33
|
+
<div class="font-mono text-sm break-words">
|
|
34
34
|
<%= json_tree(payload) %>
|
|
35
35
|
</div>
|
|
36
36
|
<% else %>
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
<h3 class="text-lg font-medium text-gray-900">Tags</h3>
|
|
48
48
|
</div>
|
|
49
49
|
<div class="p-6">
|
|
50
|
-
<div class="font-mono text-sm">
|
|
50
|
+
<div class="font-mono text-sm break-words">
|
|
51
51
|
<%= json_tree(tags) %>
|
|
52
52
|
</div>
|
|
53
53
|
</div>
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
<h3 class="text-lg font-medium text-gray-900">Context</h3>
|
|
63
63
|
</div>
|
|
64
64
|
<div class="p-6">
|
|
65
|
-
<div class="font-mono text-sm">
|
|
65
|
+
<div class="font-mono text-sm break-words">
|
|
66
66
|
<%= json_tree(context) %>
|
|
67
67
|
</div>
|
|
68
68
|
</div>
|