karafka-web 0.10.3 → 0.10.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5790e7352a06e0a780810c40aac53e9b5c878a84990c70b28bdc4d49d3ab921d
4
- data.tar.gz: e5ef8e226602ad0388b138b676d705f9dbda06363a0001b2c5e483abab9f2bed
3
+ metadata.gz: 13c431e0fa6d1b8927bd419298bc0ea522f0e823f91197fe989f72a9a08479ad
4
+ data.tar.gz: 6424f804a013bc34d1393fa79962cfacab6e6d5028726b7e90e93ada457b7a48
5
5
  SHA512:
6
- metadata.gz: a645ae9fbbe73c045562b38f744e923e3bb4ac7be47f5c49dd664a1b61513ab07706348461ef7858d3c5518a0c70c5f31addf66e146cd76e91579590e7baa268
7
- data.tar.gz: 4e5a11cd6d5333b1dff7f95856818cbe14a7a7fa172dc5cbf6d4a1409883b862dc97dc9a4799cf247023e12fdaecbae749e2d44647783af770ce3ca0cd95e51c
6
+ metadata.gz: e3ccb77dcf9624e1ca23473a963ea59cd8efc227feae098c7a80898b45c573eb70b564fd963abc6d3b0dd01a4b9e0577c1708bc2904afa6c5523360fc3880362
7
+ data.tar.gz: 60f20e2ed98fb8818164157e91ce117c5fb2d6b60f5c1371abef787f0f637269f27ed378a0ca8ffe7e95c43defddfc4749cfe1c06cbf905c8e591f8b171c2c36
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Karafka Web Changelog
2
2
 
3
+ ## 0.10.4 (2024-11-26)
4
+ - **[Breaking]** Drop Ruby `3.0` support according to the EOL schedule.
5
+ - [Enhancement] Extract producers tracking `sync_threshold` into an internal config.
6
+ - [Enhancement] Support complex Pro license loading strategies (Pro).
7
+ - [Enhancement] Change default `retention.ms` for the metrics topic to support Redpanda Cloud defaults (#450).
8
+ - [Enhancement] Include subscription group id in the consumers error tracking metadata.
9
+ - [Enhancement] Collect metadata details of low level client errors when error tracking.
10
+ - [Enhancement] Collect metadata details of low level listener errors when error tracking.
11
+ - [Fix] Toggle menu button post-turbo refresh stops working.
12
+
3
13
  ## 0.10.3 (2024-09-17)
4
14
  - **[Feature]** Introduce ability to brand Web UI with environment (Pro).
5
15
  - [Enhancement] Provide assignment status in the routing (Pro).
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka-web (0.10.3)
4
+ karafka-web (0.10.4)
5
5
  erubi (~> 1.4)
6
6
  karafka (>= 2.4.10, < 2.5.0)
7
7
  karafka-core (>= 2.4.0, < 2.5.0)
@@ -56,7 +56,7 @@ GEM
56
56
  logger (1.6.1)
57
57
  mini_portile2 (2.8.7)
58
58
  minitest (5.25.1)
59
- ostruct (0.6.0)
59
+ ostruct (0.6.1)
60
60
  raabro (1.4.0)
61
61
  rack (3.1.4)
62
62
  rack-test (2.1.0)
data/karafka-web.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_development_dependency 'rackup', '~> 0.2'
26
26
 
27
- spec.required_ruby_version = '>= 3.0.0'
27
+ spec.required_ruby_version = '>= 3.1.0'
28
28
 
29
29
  if $PROGRAM_NAME.end_with?('gem')
30
30
  spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
@@ -41,8 +41,6 @@ Gem::Specification.new do |spec|
41
41
  .reject { |f| f.start_with?('.') }
42
42
  .reject { |f| f.end_with?('.map') }
43
43
 
44
- p spec.files.select { |a| a.include?('.map')}
45
-
46
44
  spec.metadata = {
47
45
  'funding_uri' => 'https://karafka.io/#become-pro',
48
46
  'homepage_uri' => 'https://karafka.io',
@@ -76,7 +76,7 @@ module Karafka
76
76
  # Reports the metrics collected in the consumer sampler
77
77
  setting :reporter, default: Tracking::Consumers::Reporter.new
78
78
 
79
- # Minimum number of messages to produce to produce them in sync mode
79
+ # Minimum number of messages to produce them in sync mode
80
80
  # This acts as a small back-off not to overload the system in case we would have
81
81
  # extremely big number of errors and reports happening
82
82
  setting :sync_threshold, default: 50
@@ -98,6 +98,11 @@ module Karafka
98
98
  end
99
99
 
100
100
  setting :producers do
101
+ # Minimum number of messages to produce them in sync mode
102
+ # This acts as a small back-off not to overload the system in case we would have
103
+ # extremely big number of errors happening
104
+ setting :sync_threshold, default: 25
105
+
101
106
  # Reports the metrics collected in the producer sampler
102
107
  setting :reporter, default: Tracking::Producers::Reporter.new
103
108
 
@@ -41,6 +41,7 @@ module Karafka
41
41
  required(:reporter) { |val| !val.nil? }
42
42
  required(:sampler) { |val| !val.nil? }
43
43
  required(:listeners) { |val| val.is_a?(Array) }
44
+ required(:sync_threshold) { |val| val.is_a?(Integer) && val.positive? }
44
45
  end
45
46
  end
46
47
 
@@ -81,7 +81,7 @@ module Karafka
81
81
  replication_factor,
82
82
  {
83
83
  'cleanup.policy': 'compact',
84
- 'retention.ms': 60 * 60 * 1_000, # 1h
84
+ 'retention.ms': 24 * 60 * 60 * 1_000, # 1 day
85
85
  'segment.ms': 24 * 60 * 60 * 1_000, # 1 day
86
86
  'segment.bytes': 104_857_600 # 100MB
87
87
  }
@@ -17,6 +17,25 @@ module Karafka
17
17
  # Loader requires and loads all the pro components only when they are needed
18
18
  class Loader
19
19
  class << self
20
+ # This loads the pro components into memory in case someone required karafka-web prior
21
+ # to the license usage. This can happen for users with complex require flows, where
22
+ # Karafka license is not part of the standard flow
23
+ #
24
+ # In such cases Web may not notice that Karafka should operate in a Pro mode when it is
25
+ # being required via Zeitwerk. In such cases we load Pro components prior to the setup.
26
+ def load_on_late_setup
27
+ return if defined?(Karafka::Web::Pro::Commanding)
28
+
29
+ loader = Zeitwerk::Loader.new
30
+ loader.push_dir(
31
+ File.join(Karafka::Web.gem_root, 'lib/karafka/web/pro'),
32
+ namespace: Karafka::Web::Pro
33
+ )
34
+
35
+ loader.setup
36
+ loader.eager_load
37
+ end
38
+
20
39
  # Loads all the Web UI pro components and configures them wherever it is expected
21
40
  # @param config [Karafka::Core::Configurable::Node] web config that we can alter with pro
22
41
  # components
@@ -18,17 +18,24 @@ module Karafka
18
18
  #
19
19
  # @param event [Karafka::Core::Monitoring::Event]
20
20
  def on_error_occurred(event)
21
- track do |sampler|
22
- # Collect extra info if it was a consumer related error.
23
- # Those come from user code
24
- details = if event[:caller].is_a?(Karafka::BaseConsumer)
25
- extract_consumer_info(event[:caller])
26
- else
27
- {}
28
- end
21
+ caller_ref = event[:caller]
22
+
23
+ # Collect extra info if it was a consumer related error.
24
+ # Those come from user code
25
+ details = case caller_ref
26
+ when Karafka::BaseConsumer
27
+ extract_consumer_info(caller_ref)
28
+ when Karafka::Connection::Client
29
+ extract_client_info(caller_ref)
30
+ when Karafka::Connection::Listener
31
+ extract_listener_info(caller_ref)
32
+ else
33
+ {}
34
+ end
29
35
 
30
- error_class, error_message, backtrace = extract_error_info(event[:error])
36
+ error_class, error_message, backtrace = extract_error_info(event[:error])
31
37
 
38
+ track do |sampler|
32
39
  sampler.errors << {
33
40
  schema_version: SCHEMA_VERSION,
34
41
  type: event[:type],
@@ -70,6 +77,7 @@ module Karafka
70
77
  {
71
78
  topic: consumer.topic.name,
72
79
  consumer_group: consumer.topic.consumer_group.id,
80
+ subscription_group: consumer.topic.subscription_group.id,
73
81
  partition: consumer.partition,
74
82
  first_offset: consumer.messages.metadata.first_offset,
75
83
  last_offset: consumer.messages.metadata.last_offset,
@@ -80,6 +88,27 @@ module Karafka
80
88
  tags: consumer.tags
81
89
  }
82
90
  end
91
+
92
+ # @param client [::Karafka::Connection::Client]
93
+ # @return [Hash] hash with client specific info for details of error
94
+ def extract_client_info(client)
95
+ {
96
+ consumer_group: client.subscription_group.consumer_group.id,
97
+ subscription_group: client.subscription_group.id,
98
+ name: client.name,
99
+ id: client.id
100
+ }
101
+ end
102
+
103
+ # @param listener [::Karafka::Connection::Listener]
104
+ # @return [Hash] hash with listener specific info for details of error
105
+ def extract_listener_info(listener)
106
+ {
107
+ consumer_group: listener.subscription_group.consumer_group.id,
108
+ subscription_group: listener.subscription_group.id,
109
+ id: listener.id
110
+ }
111
+ end
83
112
  end
84
113
  end
85
114
  end
@@ -10,13 +10,6 @@ module Karafka
10
10
  # because there is no expectation on immediate status updates for producers and their
11
11
  # dispatch flow is always periodic based.
12
12
  class Reporter < Tracking::Reporter
13
- # Minimum number of messages to produce to produce them in sync mode
14
- # This acts as a small back-off not to overload the system in case we would have
15
- # extremely big number of errors happening
16
- PRODUCE_SYNC_THRESHOLD = 25
17
-
18
- private_constant :PRODUCE_SYNC_THRESHOLD
19
-
20
13
  # This mutex is shared between tracker and samplers so there is no case where metrics
21
14
  # would be collected same time tracker reports
22
15
  MUTEX = Mutex.new
@@ -82,7 +75,7 @@ module Karafka
82
75
  # normal operations we should not have that many messages to dispatch and it should not
83
76
  # slowdown any processing.
84
77
  def produce(messages)
85
- if messages.count >= PRODUCE_SYNC_THRESHOLD
78
+ if messages.count >= ::Karafka::Web.config.tracking.producers.sync_threshold
86
79
  ::Karafka::Web.producer.produce_many_sync(messages)
87
80
  else
88
81
  ::Karafka::Web.producer.produce_many_async(messages)
@@ -51,7 +51,7 @@ function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"f
51
51
  /*! Source: lib/karafka/web/ui/public/javascripts/charts/data_formatting_utility.js */
52
52
  let DataFormattingUtils={niceBytes(e,t=2){let i=0,s=parseInt(e,10)||0;for(;1024<=s&&++i;)s/=1024;return s.toFixed(s<10&&0<i?1:t)+" "+["bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][i]},formatLabelX(e,t){return"date"!==t?e:("00"+((t=new Date(1e3*e)).getMonth()+1)).slice(-2)+"/"+("00"+t.getDate()).slice(-2)+"/"+t.getFullYear()+" "+("00"+t.getHours()).slice(-2)+":"+("00"+t.getMinutes()).slice(-2)+":"+("00"+t.getSeconds()).slice(-2)},formatTooltip(e,t){var i=t.parsed.y,s=t.dataset.label;switch(e){case"percentage":return Math.floor(i)===i?s+": "+i+" %":s+": "+Math.round(100*i)/100+" %";case"memory":return s+": "+DataFormattingUtils.niceBytes(1024*i,2);default:return t.yLabel}},formatLabelY(e,t){switch(e){case"percentage":return Math.floor(t)===t?t+"%":Math.round(100*t)/100+"%";case"memory":return DataFormattingUtils.niceBytes(1024*t,1);default:return Math.floor(t)===t?t:Math.round(100*t)/100}},isFractionalPrecision(e){return e!==Math.floor(e)}};
53
53
  /*! Source: lib/karafka/web/ui/public/javascripts/charts/dataset_state_manager.js */class DatasetStateManager{constructor(){this.storageKey="karafkaDisabledDatasets"}readAll(){var e=localStorage.getItem(this.storageKey);return e?JSON.parse(e):{}}saveAll(e){localStorage.setItem(this.storageKey,JSON.stringify(e))}saveCurrent(){var e=document.querySelectorAll(".chartjs"),t=window.location.href.split("?")[0];let i={};var s=this.readAll();e.forEach(e=>{var e=e.id,t=Chart.getChart(e);t&&t.legend&&t.legend.legendItems&&0<(t=t.legend.legendItems.map((e,t)=>e.hidden?t:null).filter(e=>null!==e)).length&&(i[e]=t)}),s[t]=i,this.saveAll(s)}getCurrentChart(e){var t=window.location.href.split("?")[0];return(this.readAll()[t]||{})[e]||[]}}
54
- /*! Source: lib/karafka/web/ui/public/javascripts/components/btn_toggle_manager.js */class BtnToggleManager{constructor(e=".btn-toggle",t="hidden"){this.btnClass=e,this.visibilityClass=t,this.init()}init(){document.querySelectorAll(this.btnClass).forEach(t=>{let i=t.getAttribute("data-toggle-target"),s=document.getElementById(i);s&&(this.restoreVisibility(t,s),t.addEventListener("click",()=>{var e=!s.classList.contains(this.visibilityClass);s.classList.toggle(this.visibilityClass),t.classList.toggle("active",!e),this.saveVisibility(i,!e)}))})}saveVisibility(e,t){localStorage.setItem(e+"_visibility",t)}restoreVisibility(e,t){var i=localStorage.getItem(t.id+"_visibility"),i=i?"true"===i:!t.classList.contains(this.visibilityClass);t.classList.toggle(this.visibilityClass,!i),e.classList.toggle("active",i)}}
54
+ /*! Source: lib/karafka/web/ui/public/javascripts/components/btn_toggle_manager.js */class BtnToggleManager{constructor(e=".btn-toggle",t="hidden"){this.btnClass=e,this.visibilityClass=t,this.init()}init(){document.querySelectorAll(this.btnClass).forEach(t=>{let i=t.getAttribute("data-toggle-target"),s=document.getElementById(i);s&&(this.restoreVisibility(t,s),t._isClickListenerAdded||(t.addEventListener("click",()=>{var e=!s.classList.contains(this.visibilityClass);s.classList.toggle(this.visibilityClass),t.classList.toggle("active",!e),this.saveVisibility(i,!e)}),t._isClickListenerAdded=!0))})}saveVisibility(e,t){localStorage.setItem(e+"_visibility",t)}restoreVisibility(e,t){var i=localStorage.getItem(t.id+"_visibility"),i=i?"true"===i:!t.classList.contains(this.visibilityClass);t.classList.toggle(this.visibilityClass,!i),e.classList.toggle("active",i)}}
55
55
  /*! Source: lib/karafka/web/ui/public/javascripts/components/charts.js */function refreshCharts(e){(new LineChartsManager).refreshAndRender(e,!0),(new BarChartManager).refreshAndRenderBarCharts(e,!0)}function manageCharts(){(new LineChartsManager).refreshAndRender(document,!1),(new BarChartManager).refreshAndRenderBarCharts(document,!1)}
56
56
  /*! Source: lib/karafka/web/ui/public/javascripts/components/live_poll.js */var livePollTimer=null,oldDOM=null,datePicker=null,startURL=window.location.href;let isHoveringOverClickable=!1;function initLivePolling(){document.addEventListener("mouseover",function(e){isElementClickable(e.target)&&(isHoveringOverClickable=!0)}),document.addEventListener("mouseout",function(e){isElementClickable(e.target)&&(isHoveringOverClickable=!1)}),null==localStorage.karafkaLivePoll&&(localStorage.karafkaLivePoll="enabled")}function isFormActive(){var e=document.activeElement;return["INPUT","TEXTAREA","SELECT","BUTTON","FIELDSET"].includes(e.tagName)}function isElementClickable(e){return!!("A"===e.tagName||"BUTTON"===e.tagName||"INPUT"===e.tagName&&("button"===e.type||"submit"===e.type)||e.hasAttribute("onclick")||"function"==typeof e.onclick||"SPAN"===e.tagName&&e.closest(".tab-container")||e.closest("button")||e.closest("a"))}function isUserHoveringOverClickable(){return isHoveringOverClickable}function isAnyTextSelected(){var e="";return void 0!==window.getSelection?e=window.getSelection().toString():void 0!==document.selection&&"Text"==document.selection.type&&(e=document.selection.createRange().text),""!=e}function isCollapsingHappening(){return 0<document.querySelectorAll(".collapsing").length}function isPollingPossible(e=!1){return!(isFormActive()||isUserHoveringOverClickable()||isAnyTextSelected()||isOffsetLookupCalendarVisible()||isAnyModalOpen()||isCollapsingHappening()||isTurboOperating()||e&&startURL!=window.location.href)}function isAnyModalOpen(){var e;for(e of document.querySelectorAll("dialog"))if(e.open)return!0;return!1}function bindPollingButtonClick(){var e=document.getElementById("live-poll");null!=e&&e.addEventListener("click",handleLivePollingButtonClick)}function handleLivePollingButtonClick(){toggleLivePollState(),setLivePollButton(),setPollingListener()}function toggleLivePollState(){"enabled"==localStorage.karafkaLivePoll?localStorage.karafkaLivePoll="disabled":localStorage.karafkaLivePoll="enabled"}function setLivePollButton(){null!=(selector=document.getElementById("live-poll"))&&("enabled"==localStorage.karafkaLivePoll?(selector.classList.add("text-base-content"),selector.classList.remove("text-gray-500")):(selector.classList.add("text-gray-500"),selector.classList.remove("text-base-content")))}function checkResponse(e){if(e.ok)return e;throw e}function refreshPage(e){if(!isPollingPossible())return!1;var t,e=(new DOMParser).parseFromString(e,"text/html"),i=e.getElementById("content");oldDOM!=i.innerHTML&&(t=document.querySelectorAll(".chartjs").length,0==i.querySelectorAll(".chartjs").length||0==t?(document.getElementById("content").replaceWith(i),addListeners()):(t=e.getElementById("refreshable"),document.getElementById("refreshable").replaceWith(t),refreshCharts(e)),oldDOM=i.innerHTML)}function showError(e){console.error(e)}function scheduleLivePoll(){null==oldDOM&&(oldDOM=document.getElementById("content").innerHTML);let e=parseInt(localStorage.karafkaTimeInterval)||5e3;e<1e3&&(localStorage.karafkaTimeInterval=5e3,e=5e3),livePollTimer=setTimeout(livePollCallback,e)}function livePollCallback(){clearTimeout(livePollTimer),livePollTimer=null,isPollingPossible(!1)?(startURL=window.location.href,fetch(window.location.href).then(checkResponse).then(e=>e.text()).then(refreshPage).catch(showError).finally(setPollingListener)):setPollingListener()}function setPollingListener(){var e=document.getElementById("live-poll"),t=localStorage.karafkaLivePoll;"disabled"==t||null==t||null==e?(clearTimeout(livePollTimer),livePollTimer=null):(clearTimeout(livePollTimer),scheduleLivePoll())}
57
57
  /*! Source: lib/karafka/web/ui/public/javascripts/components/offset_datetime.js */function loadOffsetLookupDatePicker(){var e={locale:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear",dateFormat:"yyyy-MM-dd",timeFormat:"HH:mm",firstDay:1},timepicker:!0,onSelect:({})=>{document.getElementById("offset-lookup-datepicker").value=""},onShow:function(){offsetLookupDatePicker.selectDate((new Date).getTime()),offsetLookupDatePicker.maxDate=new Date},onHide:function(){offsetLookupDatePicker.selectDate((new Date).getTime())},buttons:[{content(e){return"Go to offset"},onClick(e){var t=e.selectedDates[0]||new Date,i=e.$el.dataset.target;e.hide(),location.href=i+"/"+formatRedirectDateTime(t)}}]};null!=offsetLookupDatePicker&&offsetLookupDatePicker.destroy(),null!=document.getElementById("offset-lookup-datepicker")&&((offsetLookupDatePicker=new AirDatepicker("#offset-lookup-datepicker",e)).maxDate=new Date,offsetLookupDatePicker.selectDate((new Date).getTime()))}function formatRedirectDateTime(e){return e.getFullYear()+`-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}/${String(e.getHours()).padStart(2,"0")}:`+String(e.getMinutes()).padStart(2,"0")}function isOffsetLookupCalendarVisible(){return null!=offsetLookupDatePicker&&offsetLookupDatePicker.visible}
@@ -61,4 +61,5 @@ let DataFormattingUtils={niceBytes(e,t=2){let i=0,s=parseInt(e,10)||0;for(;1024<
61
61
  /*! Source: lib/karafka/web/ui/public/javascripts/components/turbo_tracker.js */let turboIsOperating=!1;function isTurboOperating(){return turboIsOperating}
62
62
  /*! Source: lib/karafka/web/ui/public/javascripts/application.js */function updateTimeAgo(){0!=(e=document.querySelectorAll("time")).length&&(timeago.render(e),timeago.cancel());for(var e=document.getElementsByClassName("time-title"),t=0;t<e.length;t++){var i=e[t],s=i.getAttribute("title");i.setAttribute("title",timeago.format(s))}}function refreshTitle(){var e=document.querySelectorAll(".breadcrumbs a"),e=Array.from(e).slice(1).map(e=>e.textContent.trim());0<e.length?document.title=e.join(" > ")+" - Karafka Web UI":document.title="Karafka Web UI"}function redirectToPartition(){var e=document.getElementById("current-partition");null!=e&&e.addEventListener("change",function(){Turbo.visit(this.value)})}function bindActionsConfirmations(){for(var t=document.getElementsByClassName("confirm-action"),i=0;i<t.length;i++){var s=t[i];let e="click";"FORM"===s.nodeName&&(e="submit"),s.addEventListener(e,function(e){window.confirm("Are you sure?")||e.preventDefault()})}}function bindLockableButtons(){document.querySelectorAll(".btn-lockable").forEach(function(i){i.addEventListener("click",function(e){var t=i.closest("form");t?t.addEventListener("submit",function(){i.disabled=!0,i.textContent+="...",document.querySelectorAll(".modal").forEach(function(e){e.classList.add("modal-locked")})},{once:!0}):(i.disabled=!0,i.textContent+="...")})})}function addListeners(){initLivePolling(),bindPollingButtonClick(),bindLockableButtons(),setLivePollButton(),setPollingListener(),hljs.highlightAll(),updateTimeAgo(),redirectToPartition(),(new TabsManager).manageTabs(),manageCharts(),bindActionsConfirmations(),loadOffsetLookupDatePicker(),new BtnToggleManager,new BtnToggleManager(".btn-toggle-nav-collapsed","collapsed"),new ThemeManager,refreshTitle(),new SearchMetadataVisibilityManager,new SearchModalManager}document.addEventListener("turbo:visit",function(){turboIsOperating=!0}),document.addEventListener("turbo:before-fetch-request",function(){turboIsOperating=!0}),document.addEventListener("turbo:before-fetch-response",function(){turboIsOperating=!0}),document.addEventListener("turbo:load",function(){turboIsOperating=!1}),document.addEventListener("turbo:frame-load",function(){turboIsOperating=!1}),document.addEventListener("turbo:frame-render",function(){turboIsOperating=!1}),document.addEventListener("turbo:load",addListeners),Turbo.setProgressBarDelay(100)
63
63
  /*! Source: lib/karafka/web/ui/public/javascripts/charts/types/bar.js */;class BarChartManager{constructor(){this.datasetStateManager=new DatasetStateManager}refreshAndRenderBarCharts(c,u=!1){(u?c:document).querySelectorAll(".chartjs-bar").forEach(e=>{var t=e.id,i=(u?c:document).getElementById(t),s=JSON.parse(i.dataset.datasets);let r=[],a=[],n=[];var o=i.dataset.label_type_y;let l=i.dataset.label_type_x,h=this.datasetStateManager.getCurrentChart(t);Object.entries(s).forEach(([e,t],i)=>{t.forEach(([e,t])=>{n.push(t),0===i&&r.push(DataFormattingUtils.formatLabelX(e,l))}),a.push({data:t.map(([,e])=>e),label:e,hidden:h.includes(i),borderWidth:2.5})});var i=Math.min(...n),s=Math.max(...n),i=Math.round(i-.1*i),s=Math.round(s+.005*s),d=Math.round(n.reduce((e,t)=>e+t,0)/n.length);a.push({type:"line",label:"Average",data:new Array(r.length).fill(d),borderWidth:2,fill:!1,pointRadius:0,hoverBorderWidth:3,pointHitRadius:20}),u?((d=Chart.getChart(t)).data.datasets=a,d.data.labels=r,d.options.scales.y.min=i,d.options.scales.y.max=s,d.update("none")):this.renderBarChart(e,r,a,i,s,o)})}renderBarChart(e,t,i,s,r,a){new Chart(e,{type:"bar",data:{labels:t,datasets:i},options:{responsive:!0,maintainAspectRatio:!1,aspectRatio:5,scales:{x:{display:!0},y:{beginAtZero:!1,min:s,max:r,ticks:{maxTicksLimit:8,callback:function(e,t,i){return DataFormattingUtils.formatLabelY(a,e,t,i)}}}},animation:!1,animations:{colors:!1,x:!1},transitions:{active:{animation:{duration:!1}}},plugins:{legend:{position:"hidden"},tooltip:{callbacks:{label:function(e){let t=e.dataset.label||"";return"Average"!==t?DataFormattingUtils.formatTooltip(a,e):t+=": "+e.formattedValue}}}}}})}}
64
- /*! Source: lib/karafka/web/ui/public/javascripts/charts/types/line.js */class LineChartsManager{constructor(){this.datasetStateManager=new DatasetStateManager}getLegendHeightPercentage(e){var t=e.chartArea,e=e.height,t=e-(t.bottom-t.top);return Math.round(t/e*100)}afterRenderPlugin(){let i=this;return{id:"afterRender",afterRender:function(e){var t=i.getLegendHeightPercentage(e),e=document.getElementById(e.canvas.id);50<t&&""==e.parentElement.style.height&&(e.parentElement.style.height="400px")}}}refreshAndRender(d,c=!1){(c?d:document).querySelectorAll(".chartjs-line").forEach(e=>{var t=e.id,i=(c?d:document).getElementById(t),s=JSON.parse(i.dataset.datasets);let r=[],a=[],n=0;var o=i.dataset.label_type_y;let l=i.dataset.label_type_x,h=this.datasetStateManager.getCurrentChart(t);Object.entries(s).forEach(([e,t],i)=>{t.forEach(([e,t])=>{0===i&&r.push(DataFormattingUtils.formatLabelX(e,l)),DataFormattingUtils.isFractionalPrecision(t)&&(n=2)}),a.push({data:t,label:e,hidden:h.includes(i),borderWidth:2.5,pointHitRadius:10})}),c?((i=Chart.getChart(t)).data.datasets=a,i.data.labels=r,i.update("none")):this.render(e,r,a,n,o)})}render(e,t,i,s,r){var a=null,a=10<i.length?"point":"x";new Chart(e,{type:"line",data:{labels:t,datasets:i},options:{responsive:!0,maintainAspectRatio:!1,aspectRatio:5,title:{display:!1},interaction:{mode:"nearest",axis:"x",intersect:!1},animation:!1,transitions:{active:{animation:{duration:!1}}},plugins:{legend:{position:"bottom",labels:{padding:20},onClick:(e,t,i)=>{var s=t.datasetIndex,i=i.chart;i.isDatasetVisible(s)?(i.hide(s),t.hidden=!0):(i.show(s),t.hidden=!1),this.datasetStateManager.saveCurrent()}},tooltip:{mode:a,filter:function(e,t,i){return t<10},callbacks:{label:function(e){return DataFormattingUtils.formatTooltip(r,e)}}}},scales:{x:{display:!1},y:{ticks:{precision:s,count:5,callback:function(e,t,i){return DataFormattingUtils.formatLabelY(r,e,t,i)}}}},elements:{point:{radius:0,style:!1},line:{style:"star",radius:0,spanGaps:!1}},hover:{mode:"index",intersect:!1}},plugins:[this.afterRenderPlugin()]})}}
64
+ /*! Source: lib/karafka/web/ui/public/javascripts/charts/types/line.js */class LineChartsManager{constructor(){this.datasetStateManager=new DatasetStateManager}getLegendHeightPercentage(e){var t=e.chartArea,e=e.height,t=e-(t.bottom-t.top);return Math.round(t/e*100)}afterRenderPlugin(){let i=this;return{id:"afterRender",afterRender:function(e){var t=i.getLegendHeightPercentage(e),e=document.getElementById(e.canvas.id);50<t&&""==e.parentElement.style.height&&(e.parentElement.style.height="400px")}}}refreshAndRender(d,c=!1){(c?d:document).querySelectorAll(".chartjs-line").forEach(e=>{var t=e.id,i=(c?d:document).getElementById(t),s=JSON.parse(i.dataset.datasets);let r=[],a=[],n=0;var o=i.dataset.label_type_y;let l=i.dataset.label_type_x,h=this.datasetStateManager.getCurrentChart(t);Object.entries(s).forEach(([e,t],i)=>{t.forEach(([e,t])=>{0===i&&r.push(DataFormattingUtils.formatLabelX(e,l)),DataFormattingUtils.isFractionalPrecision(t)&&(n=2)}),a.push({data:t,label:e,hidden:h.includes(i),borderWidth:2.5,pointHitRadius:10})}),c?((i=Chart.getChart(t)).data.datasets=a,i.data.labels=r,i.update("none")):this.render(e,r,a,n,o)})}render(e,t,i,s,r){var a=null,a=10<i.length?"point":"x";new Chart(e,{type:"line",data:{labels:t,datasets:i},options:{responsive:!0,maintainAspectRatio:!1,aspectRatio:5,title:{display:!1},interaction:{mode:"nearest",axis:"x",intersect:!1},animation:!1,transitions:{active:{animation:{duration:!1}}},plugins:{legend:{position:"bottom",labels:{padding:20},onClick:(e,t,i)=>{var s=t.datasetIndex,i=i.chart;i.isDatasetVisible(s)?(i.hide(s),t.hidden=!0):(i.show(s),t.hidden=!1),this.datasetStateManager.saveCurrent()}},tooltip:{mode:a,filter:function(e,t,i){return t<10},callbacks:{label:function(e){return DataFormattingUtils.formatTooltip(r,e)}}}},scales:{x:{display:!1},y:{ticks:{precision:s,count:5,callback:function(e,t,i){return DataFormattingUtils.formatLabelY(r,e,t,i)}}}},elements:{point:{radius:0,style:!1},line:{style:"star",radius:0,spanGaps:!1}},hover:{mode:"index",intersect:!1}},plugins:[this.afterRenderPlugin()]})}}
65
+ //# sourceMappingURL=application.min.js.map
@@ -6,6 +6,7 @@
6
6
  'hidden' class on their target elements (defined by the data attribute `data-toggle-target`)
7
7
  when clicked.
8
8
  */
9
+
9
10
  class BtnToggleManager {
10
11
  constructor(btnClass = '.btn-toggle', visibilityClass = 'hidden') {
11
12
  this.btnClass = btnClass;
@@ -23,13 +24,22 @@ class BtnToggleManager {
23
24
  // Establish initial state from local storage or based on visibility
24
25
  this.restoreVisibility(button, targetElement);
25
26
 
26
- // Add event listener to toggle visibility
27
- button.addEventListener('click', () => {
28
- const isVisible = !targetElement.classList.contains(this.visibilityClass);
29
- targetElement.classList.toggle(this.visibilityClass);
30
- button.classList.toggle('active', !isVisible);
31
- this.saveVisibility(targetId, !isVisible);
32
- });
27
+ // Check if the event listener has already been added
28
+ if (!button._isClickListenerAdded) {
29
+ // Define the event handler and store the flag to prevent duplicate listeners
30
+ const handleClick = () => {
31
+ const isVisible = !targetElement.classList.contains(this.visibilityClass);
32
+ targetElement.classList.toggle(this.visibilityClass);
33
+ button.classList.toggle('active', !isVisible);
34
+ this.saveVisibility(targetId, !isVisible);
35
+ };
36
+
37
+ // Add the event listener
38
+ button.addEventListener('click', handleClick);
39
+
40
+ // Set flag to indicate the listener has been added
41
+ button._isClickListenerAdded = true;
42
+ }
33
43
  });
34
44
  }
35
45