kanaui 4.0.4 → 4.0.5

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.
@@ -57,9 +57,23 @@
57
57
  fill: #202020;
58
58
  }
59
59
 
60
- .chart-title{
61
- padding: 20px;
62
- text-indent: 30px;
60
+ .chart-title-container {
61
+ height: 31.25rem;
62
+ width: 1.25rem;
63
+ display: flex;
64
+ justify-content: center;
65
+ align-items: center;
66
+ position: relative;
67
+ }
68
+
69
+ .chart-title {
70
+ font-weight: 500;
71
+ font-size: 0.75rem;
72
+ line-height: 1.125rem;
73
+ text-align: center;
74
+ color: #535862;
75
+ white-space: nowrap;
76
+ transform: rotate(-90deg);
63
77
  }
64
78
 
65
79
  .nav-element.current{
@@ -19,6 +19,22 @@ module Kanaui
19
19
  @reports = JSON.parse(raw_reports)
20
20
  @report = current_report(@reports) || {}
21
21
 
22
+ # If no report name is provided, redirect to the default (second) report
23
+ if @raw_name.blank? && @reports.is_a?(Array) && @reports[1].present?
24
+ default_name = @reports[1]['reportName']
25
+ query_params = { start_date: @start_date,
26
+ end_date: @end_date,
27
+ name: default_name,
28
+ smooth: params[:smooth],
29
+ sql_only: params[:sql_only],
30
+ format: params[:format] }
31
+
32
+ query_params[:fake] = params[:fake] unless params[:fake].blank?
33
+ query_params[:type] = params[:type] unless params[:type].blank?
34
+
35
+ redirect_to dashboard_index_path(query_params) and return
36
+ end
37
+
22
38
  query = build_slice_and_dice_query
23
39
 
24
40
  # get columns visibility from query parameters to be used by tables
@@ -1,193 +1,316 @@
1
- <div class="flex-panel">
2
- <!-- MENU -->
3
- <div class="reports-options flex-inner-left-panel">
4
- <div class="row">
5
- <div class="col-md-12 col-sm-8">
6
- <div class="calendar-container">
7
- <span class="calendar-icon"><i class="fa fa-calendar"></i><i class="fa fa-caret-down"></i></span>
8
- <% if params[:name] %>
9
- <div class="form-container">
10
- <%= form_tag kanaui_engine.dashboard_index_path, :class => 'form-horizontal', :method => :get do %>
11
- <input name="name" type="hidden" value="<%= @raw_name %>">
12
- <input name="smooth" type="hidden" value="<%= params[:smooth] %>">
13
- <input name="sql_only" type="hidden" value="<%= params[:sql_only] %>">
14
- <input name="format" type="hidden" value="<%= params[:format] %>">
15
- <input name="delta_days" type="hidden" value="<%= (@end_date.to_date - @start_date.to_date).to_i %>">
16
- <div class="form-group">
17
- <div id="dp1" class="col-md-7">
18
- From:<input class="form-control datepicker" name="start_date" type="text" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-today-highlight="true" value="<%= @start_date %>">
19
- </div>
20
- </div>
21
- <div class="form-group">
22
- <div class="col-md-7">
23
- To:<input class="form-control" name="end_date" type="text" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-today-highlight="true" value="<%= @end_date %>">
24
- </div>
25
- </div>
26
- <% ((@report['variables'] || {})['fields'] || []).each do |definition| %>
27
- <div class="form-group">
28
- <div class="col-md-7">
29
- <%= definition['name'].titleize %>:
30
- <% # TODO Not fully implemented server side %>
31
- <% if definition['dataType'] == 'date' %>
32
- <% # TODO datepicker breaks :hover %>
33
- <input class="form-control" name="variable_<%= definition['name'] %>" type="text" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-today-highlight="true" value=""/>
34
- <% else %>
35
- <input class="form-control" name="variable_<%= definition['name'] %>" type="text" value=""/>
36
- <% end %>
37
- </div>
38
- </div>
39
- <% end %>
40
- <div class="form-group">
41
- <div class="col-sm-12">
42
- <%= submit_tag 'Refresh', :class => 'btn btn-default' %>
43
- </div>
44
- </div>
45
- <% end %>
46
- </div>
47
- <% end %>
48
- </div>
49
- <div class="col-md-12 col-sm-8">
50
- <h4><%= link_to 'Available Reports', kanaui_engine.url_for(:controller => :reports) %></h4>
51
- <ul class="nav nav-tabs nav-stacked">
52
- <% if Rails.env.development? && @reports.empty? %>
53
- <li class="nav-element">
54
- <%= link_to "Fake pie", kanaui_engine.dashboard_index_path(:fake => 1, :name => 'fake_pie', :type => 'pie') %>
55
- </li>
56
- <li class="nav-element">
57
- <%= link_to "Fake line", kanaui_engine.dashboard_index_path(:fake => 1, :name => 'fake_line', :type => 'line') %>
58
- </li>
59
- <% end %>
60
- <% @reports.each do |r| %>
61
- <% link = kanaui_engine.dashboard_index_path(params.to_h.merge(:name => r['reportName'])) %>
62
- <li class="nav-element <%= params[:name] == r['reportName'] ? 'current' : '' %>">
63
- <%= link_to r['reportPrettyName'], link, :class => "truncate-text", title: r['reportName'].titleize %>
64
- </li>
65
- <% end %>
66
- </ul>
67
- </div>
68
- </div>
69
- </div>
70
- <div class="row">
71
- <div class="col-md-12 col-sm-8">
72
- <h5><%= link_to 'Settings', kanaui_engine.url_for(:controller => :settings) %></h5>
73
- </div>
74
- </div>
75
- </div>
76
1
 
77
- <!-- DASHBOARD -->
78
- <div class="flex-inner-right-panel">
79
- <div class="row">
80
- <div class="col-sm-12">
81
- <% if params[:name] %>
82
- <h2 class="chart-title">
83
- <%= @raw_name.titleize %>
84
- </h2>
85
- <div id="loading-spinner"></div>
86
- <div id="chartAnchor" data-reports-path="<%= kanaui_engine.reports_path(params.to_h) %>"></div>
87
- <div id="date-controls" style="display: none;">
88
- <ul class="nav nav-pills nav-justified">
89
- <% @available_start_dates.each do |key, value| %>
90
- <li><%= link_to key, kanaui_engine.dashboard_index_path(params.to_h.merge(:start_date => value)) %></li>
91
- <% end %>
92
- </ul>
93
- </div>
94
- <hr>
95
- <div class="pull-right">
96
- <% if @report['reportType'] == 'TABLE' %>
97
- <input type="hidden" id="visible-table-columns" value="<%= @visible_columns %>">
98
- <a class="btn btn-default" id="copy-url" data-reports-path="<%= kanaui_engine.dashboard_index_path(params.to_h) %>">Copy URL</a>
99
- <input id="url-placeholder" class="form-control hidden">
100
- <% end %>
101
- <%= link_to 'Download raw data', kanaui_engine.reports_path(params.to_h.merge(:format => 'csv')), class: 'btn btn-default' %>
102
- </div>
103
- <a class="btn btn-default" role="button" data-toggle="collapse" href="#advanced-controls" aria-expanded="false" aria-controls="advanced-controls">
104
- Advanced controls
105
- </a>
106
- <div class="collapse" id="advanced-controls">
107
- <div class="well">
108
- <ul>
109
- <% if @report['reportType'] == 'TIMELINE' %>
110
- <% at_least_two_months = params[:start_date].blank? || params[:end_date].blank? || (params[:end_date].to_date.beginning_of_month - 1.month > params[:start_date].to_date) %>
111
- <% at_least_two_weeks = params[:start_date].blank? || params[:end_date].blank? || (params[:end_date].to_date.beginning_of_week - 1.week > params[:start_date].to_date) %>
112
- <% if params[:smooth] != 'AVERAGE_WEEKLY' && at_least_two_weeks %>
113
- <li><%= link_to 'Weekly average', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'AVERAGE_WEEKLY')) %></li>
114
- <% end %>
115
- <% if params[:smooth] != 'AVERAGE_MONTHLY' && at_least_two_months %>
116
- <li><%= link_to 'Monthly average', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'AVERAGE_MONTHLY')) %></li>
117
- <% end %>
118
- <% if params[:smooth] != 'SUM_WEEKLY' && at_least_two_weeks %>
119
- <li><%= link_to 'Weekly sum', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'SUM_WEEKLY')) %></li>
120
- <% end %>
121
- <% if params[:smooth] != 'SUM_MONTHLY' && at_least_two_months %>
122
- <li><%= link_to 'Monthly sum', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'SUM_MONTHLY')) %></li>
2
+ <div class="kaui-container kenui-analytics-dashboard-index">
3
+ <%= render partial: 'kaui/components/breadcrumb/breadcrumb', locals: {
4
+ breadcrumbs: [
5
+ { label: 'Settings', href: '/' },
6
+ { label: "Analytics", href: '#' }
7
+ ]
8
+ } %>
9
+
10
+ <div class="d-flex" style="gap: 4rem;">
11
+ <%= render template: 'kaui/layouts/kaui_setting_sidebar',
12
+ locals: {
13
+ identifier: "Settings",
14
+ controller: params[:controller],
15
+ menu_items: [
16
+ { label: 'Account', path: kaui_engine.admin_tenant_path(session[:tenant_id]), controller_suffix: "#{session[:tenant_id]}", icon_path: 'kaui/setting/account.svg' },
17
+ { label: 'Tenants', path: kaui_engine.admin_tenants_path, controller_suffix: 'admin_tenants', icon_path: 'kaui/setting/tenants.svg' },
18
+ { label: 'Users', path: kaui_engine.admin_allowed_users_path, controller_suffix: 'admin_allowed_users', icon_path: 'kaui/setting/users.svg' },
19
+ { label: 'Tags', path: kaui_engine.tags_path, controller_suffix: 'tags', icon_path: 'kaui/setting/tags.svg' },
20
+ { label: 'Tag Definitions', path: kaui_engine.tag_definitions_path, controller_suffix: 'tag_definitions', icon_path: 'kaui/setting/tag-definitions.svg' },
21
+ { label: 'Custom Fields', path: kaui_engine.custom_fields_path, controller_suffix: 'custom_fields', icon_path: 'kaui/setting/custom-field.svg' },
22
+ { label: 'Plugin Manager', path: "/kpm", controller_suffix: 'kpm', icon_path: 'kaui/setting/plugins.svg' },
23
+ { label: 'E-notifications', path: "/kenui", controller_suffix: 'kenui', icon_path: 'kaui/setting/e-notifications.svg' },
24
+ { label: 'Analytics', path: "/analytics", controller_suffix: 'analytics', icon_path: 'kaui/setting/analytics.svg' },
25
+ ]
26
+ }
27
+ %>
28
+
29
+ <div class="analytics w-100">
30
+ <div class="d-flex flex-column">
31
+ <div class="analytics-header">
32
+ <h2>Analytics</h2>
33
+ <span class="d-flex">
34
+
35
+ <div class="d-flex align-items-center me-4">
36
+ <b class="me-2"><%= link_to 'Available Reports', kanaui_engine.url_for(:controller => :reports) %>:</b>
37
+ <% if Rails.env.development? && @reports.empty? %>
38
+ <ul class="">
39
+ <li class="nav-element">
40
+ <%= link_to "Fake pie", kanaui_engine.dashboard_index_path(fake: 1, name: 'fake_pie', type: 'pie') %>
41
+ </li>
42
+ <li class="nav-element">
43
+ <%= link_to "Fake line", kanaui_engine.dashboard_index_path(fake: 1, name: 'fake_line', type: 'line') %>
44
+ </li>
45
+ </ul>
46
+ <% else %>
47
+ <select class="form-select" id="report-select" onchange="goToReport(this.value);">
48
+ <% @reports.each_with_index do |r, idx| %>
49
+ <% report_link = kanaui_engine.dashboard_index_path(params.to_h.merge(name: r['reportName'])) %>
50
+ <option value="<%= report_link %>" <%= 'selected' if @raw_name == r['reportName'] || (@raw_name.blank? && idx == 1) %>>
51
+ <%= r['reportPrettyName'] %>
52
+ </option>
123
53
  <% end %>
124
- <% end %>
125
- <% filter_fields = ((@report['schema'] || {})['fields'] || []).select { |field| !field['distinctValues'].blank? && field['dataType'] =~ /char/ } # To ignore tenant_record_id %>
126
- <% unless filter_fields.empty? %>
127
- <li>Slicing & Dicing:
128
- <%= form_tag kanaui_engine.dashboard_index_path(params.to_h), :method => :get, :class => 'form-horizontal' do %>
129
- <input name="start_date" type="hidden" value="<%= @start_date %>">
130
- <input name="end_date" type="hidden" value="<%= @end_date %>">
131
- <input name="name" type="hidden" value="<%= @raw_name %>">
132
- <input name="smooth" type="hidden" value="<%= params[:smooth] %>">
133
- <input name="sql_only" type="hidden" value="<%= params[:sql_only] %>">
134
- <input name="format" type="hidden" value="<%= params[:format] %>">
135
-
136
- <fieldset class="form-group">
137
- <legend>Filters</legend>
138
- <% filter_fields.each do |field| %>
139
- <div class="form-group">
140
- <%= label_tag "filter_#{field['name']}", field['name'], :class => 'col-sm-2 control-label' %>
141
- <div class="col-sm-10">
142
- <%= select_tag "filter_#{field['name']}", options_for_select(field['distinctValues']), :multiple => true, :class => 'form-control' %>
143
- </div>
144
- </div>
145
- <% end %>
146
- </fieldset>
147
- <fieldset class="form-group">
148
- <legend>Dimensions to plot</legend>
149
- <% filter_fields.each do |field| %>
150
- <div class="form-group">
151
- <%= label_tag "group_#{field['name']}", field['name'], :class => 'col-sm-2 control-label' %>
152
- <div class="col-sm-10">
153
- <%= select_tag "group_#{field['name']}", options_for_select(field['distinctValues']), :multiple => true, :class => 'form-control' %>
154
- </div>
155
- </div>
156
- <% end %>
157
- </fieldset>
54
+ </select>
55
+ <% end %>
56
+
57
+ </div>
58
+
59
+
60
+ <div class="calendar-container">
61
+ <% if params[:name] %>
62
+ <div class="form-container d-flex">
63
+ <%= form_tag kanaui_engine.dashboard_index_path, :class => 'form-vertical d-flex', :method => :get do %>
64
+ <div class="trigger-invoice-box form-group">
65
+ <input name="name" type="hidden" value="<%= @raw_name %>">
66
+ <input name="smooth" type="hidden" value="<%= params[:smooth] %>">
67
+ <input name="sql_only" type="hidden" value="<%= params[:sql_only] %>">
68
+ <input name="format" type="hidden" value="<%= params[:format] %>">
69
+ <input name="delta_days" type="hidden" value="<%= (@end_date.to_date - @start_date.to_date).to_i %>">
70
+ <div class="d-flex align-items-center">
71
+ <div class="d-flex gap-2">
72
+ <div class="form-group d-flex align-items-center gap-2" id="dp1">
73
+ <b>From:</b>
74
+ <div class="custom-date-input-wrapper kaui-button custom-hover">
75
+ <svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
76
+ <path d="M14.9167 12.5V18.3333M17.8333 15.4167H12" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
77
+ <path d="M14.0833 1.66675V5.00008M6.58325 1.66675V5.00008" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
78
+ <path d="M17.8333 10.8333V4.99992C17.8333 4.07944 17.0871 3.33325 16.1666 3.33325H4.49992C3.57944 3.33325 2.83325 4.07944 2.83325 4.99992V16.6666C2.83325 17.5871 3.57944 18.3333 4.49992 18.3333H10.3333" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
79
+ <path d="M2.83325 8.33325H17.8333" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
80
+ </svg>
81
+
82
+ <input class="form-control datepicker" name="start_date" type="text" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-today-highlight="true" value="<%= @start_date %>">
83
+ </div>
84
+ </div>
85
+ <div class="form-group d-flex align-items-center gap-2">
86
+ <b>To:</b>
87
+ <div class="custom-date-input-wrapper kaui-button custom-hover">
88
+ <svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
89
+ <path d="M14.9167 12.5V18.3333M17.8333 15.4167H12" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
90
+ <path d="M14.0833 1.66675V5.00008M6.58325 1.66675V5.00008" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
91
+ <path d="M17.8333 10.8333V4.99992C17.8333 4.07944 17.0871 3.33325 16.1666 3.33325H4.49992C3.57944 3.33325 2.83325 4.07944 2.83325 4.99992V16.6666C2.83325 17.5871 3.57944 18.3333 4.49992 18.3333H10.3333" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
92
+ <path d="M2.83325 8.33325H17.8333" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
93
+ </svg>
158
94
 
159
- <div class="form-group">
160
- <div class="col-sm-offset-2 col-sm-10">
161
- <%= submit_tag 'Refresh', :class => 'btn btn-default' %>
95
+ <input class="form-control datepicker" name="end_date" type="text" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-today-highlight="true" value="<%= @end_date %>">
96
+ </div>
162
97
  </div>
98
+ <% ((@report['variables'] || {})['fields'] || []).each do |definition| %>
99
+ <div class="form-group">
100
+ <div class="col-md-7">
101
+ <%= definition['name'].titleize %>:
102
+ <% # TODO Not fully implemented server side %>
103
+ <% if definition['dataType'] == 'date' %>
104
+ <% # TODO datepicker breaks :hover %>
105
+ <input class="form-control" name="variable_<%= definition['name'] %>" type="text" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-today-highlight="true" value=""/>
106
+ <% else %>
107
+ <input class="form-control" name="variable_<%= definition['name'] %>" type="text" value=""/>
108
+ <% end %>
109
+ </div>
110
+ </div>
111
+ <% end %>
163
112
  </div>
164
- <% end %>
165
- </li>
166
- <li>Current Analytics query:&nbsp;<%= link_to '<i class="fa fa-question-circle"></i>'.html_safe, 'http://docs.killbill.io/latest/userguide_analytics.html#_dashboard_api', :target => '_blank' %>
167
- <pre><%= params[:name] -%></pre>
168
- </li>
113
+
114
+
115
+ <%= render "kaui/components/button/button", {
116
+ label: "Refresh",
117
+ icon: "kaui/setting/reset.svg",
118
+ variant: "outline-secondary d-inline-flex align-items-center gap-1",
119
+ type: "submit",
120
+ html_class: "btn btn-xs kaui-dropdown custom-hover py-2 px-3 refresh-button",
121
+ } %>
122
+
123
+ <%= link_to kanaui_engine.url_for(:controller => :reports), class: 'text-decoration-none' do %>
124
+ <%= render "kaui/components/button/button", {
125
+ label: "Configure reports",
126
+ variant: "outline-secondary d-inline-flex align-items-center gap-1",
127
+ type: "button",
128
+ html_class: "btn btn-xs kaui-dropdown custom-hover py-2 px-3 refresh-button",
129
+ } %>
130
+ <% end %>
131
+ </div>
132
+ </div>
133
+ <% end %>
134
+ </div>
169
135
  <% end %>
170
- <li><%= link_to 'SQL query', kanaui_engine.reports_path(params.to_h.merge(:sql_only => true)) %></li>
171
- </ul>
136
+ </div>
137
+ </span>
138
+ </div>
139
+
140
+ <div class="analytics w-100">
141
+ <div class="flex-panel kaui-container w-100">
142
+
143
+ <div class="w-100">
144
+ <div class="row">
145
+ <div class="">
146
+ <% if params[:name] %>
147
+
148
+ <div id="date-controls" style="display: none;">
149
+ <div class="custom-tabs my-4">
150
+ <% @available_start_dates.each do |key, value| %>
151
+ <%= link_to key, kanaui_engine.dashboard_index_path(params.to_h.merge(:start_date => value)), class: "custom-tab #{'activelink' if params[:start_date] == value}" %>
152
+ <% end %>
153
+ </div>
154
+ </div>
155
+ <div id="loading-spinner"></div>
156
+ <div class="d-flex w-100 align-items-center">
157
+ <div class="chart-title-container">
158
+ <h2 class="chart-title" id="chart-title">
159
+ <%= @raw_name.titleize %>
160
+ </h2>
161
+ </div>
162
+ <div id="chartAnchor" data-reports-path="<%= kanaui_engine.reports_path(params.to_h) %>" class="w-100 h-100"></div>
163
+ </div>
164
+
165
+ <hr>
166
+
167
+ <div class="pull-right d-flex align-items-center gap-3 justify-content-end">
168
+ <%= link_to kanaui_engine.url_for(controller: :settings), class: 'text-decoration-none' do %>
169
+ <%= render "kaui/components/button/button", {
170
+ label: "Settings",
171
+ variant: "outline-secondary d-inline-flex align-items-center gap-1",
172
+ type: "button",
173
+ html_class: "btn btn-xs kaui-dropdown custom-hover py-2 px-3 refresh-button"
174
+ } %>
175
+ <% end %>
176
+ <% if @report['reportType'] == 'TABLE' %>
177
+ <input type="hidden" id="visible-table-columns" value="<%= @visible_columns %>">
178
+ <a class="btn btn-default p-0 py-2" id="copy-url" data-reports-path="<%= kanaui_engine.dashboard_index_path(params.to_h) %>">Copy URL</a>
179
+ <input id="url-placeholder" class="form-control hidden url-placeholder-input">
180
+ <% end %>
181
+ <%= link_to kanaui_engine.reports_path(params.to_h.merge(:format => 'csv')), class: 'text-decoration-none' do %>
182
+ <%= render "kaui/components/button/button", {
183
+ label: "Download raw data",
184
+ variant: "outline-secondary d-inline-flex align-items-center gap-1",
185
+ type: "button",
186
+ html_class: "btn btn-xs kaui-dropdown custom-hover py-2 px-3 refresh-button"
187
+ } %>
188
+ <% end %>
189
+ </div>
190
+ <a class="" role="button" data-bs-toggle="collapse" href="#advanced-controls" aria-expanded="false" aria-controls="advanced-controls">
191
+ <%= render "kaui/components/button/button", {
192
+ label: "Advanced controls",
193
+ variant: "outline-secondary d-inline-flex align-items-center gap-1",
194
+ type: "button",
195
+ html_class: "btn btn-xs kaui-dropdown custom-hover py-2 px-3 refresh-button"
196
+ } %>
197
+ </a>
198
+ <div class="collapse" id="advanced-controls">
199
+ <div class="well">
200
+ <ul class="">
201
+ <% if @report['reportType'] == 'TIMELINE' %>
202
+ <% at_least_two_months = params[:start_date].blank? || params[:end_date].blank? || (params[:end_date].to_date.beginning_of_month - 1.month > params[:start_date].to_date) %>
203
+ <% at_least_two_weeks = params[:start_date].blank? || params[:end_date].blank? || (params[:end_date].to_date.beginning_of_week - 1.week > params[:start_date].to_date) %>
204
+ <% if params[:smooth] != 'AVERAGE_WEEKLY' && at_least_two_weeks %>
205
+ <li><%= link_to 'Weekly average', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'AVERAGE_WEEKLY')) %></li>
206
+ <% end %>
207
+ <% if params[:smooth] != 'AVERAGE_MONTHLY' && at_least_two_months %>
208
+ <li><%= link_to 'Monthly average', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'AVERAGE_MONTHLY')) %></li>
209
+ <% end %>
210
+ <% if params[:smooth] != 'SUM_WEEKLY' && at_least_two_weeks %>
211
+ <li><%= link_to 'Weekly sum', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'SUM_WEEKLY')) %></li>
212
+ <% end %>
213
+ <% if params[:smooth] != 'SUM_MONTHLY' && at_least_two_months %>
214
+ <li><%= link_to 'Monthly sum', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'SUM_MONTHLY')) %></li>
215
+ <% end %>
216
+ <% end %>
217
+ <% filter_fields = ((@report['schema'] || {})['fields'] || []).select { |field| !field['distinctValues'].blank? && field['dataType'] =~ /char/ } # To ignore tenant_record_id %>
218
+ <% unless filter_fields.empty? %>
219
+ <li class="advanced-controls">
220
+ <span>Slicing & Dicing:</span>
221
+ <%= form_tag kanaui_engine.dashboard_index_path(params.to_h), :method => :get, :class => 'form-horizontal' do %>
222
+ <input name="start_date" type="hidden" value="<%= @start_date %>">
223
+ <input name="end_date" type="hidden" value="<%= @end_date %>">
224
+ <input name="name" type="hidden" value="<%= @raw_name %>">
225
+ <input name="smooth" type="hidden" value="<%= params[:smooth] %>">
226
+ <input name="sql_only" type="hidden" value="<%= params[:sql_only] %>">
227
+ <input name="format" type="hidden" value="<%= params[:format] %>">
228
+
229
+ <fieldset>
230
+ <legend>Filters</legend>
231
+ <% filter_fields.each do |field| %>
232
+ <div class="form-group">
233
+ <%= label_tag "filter_#{field['name']}", field['name'], :class => 'control-label' %>
234
+ <div class="w-100">
235
+ <%= select_tag "filter_#{field['name']}", options_for_select(field['distinctValues']), :multiple => true, :class => 'form-control' %>
236
+ </div>
237
+ </div>
238
+ <% end %>
239
+ </fieldset>
240
+ <fieldset>
241
+ <legend>Dimensions to plot</legend>
242
+ <% filter_fields.each do |field| %>
243
+ <div class="form-group">
244
+ <%= label_tag "group_#{field['name']}", field['name'], :class => 'control-label' %>
245
+ <div class="w-100">
246
+ <%= select_tag "group_#{field['name']}", options_for_select(field['distinctValues']), :multiple => true, :class => 'form-control' %>
247
+ </div>
248
+ </div>
249
+ <% end %>
250
+ </fieldset>
251
+
252
+ <div class="form-group mb-0">
253
+ <%= submit_tag 'Refresh', :class => 'btn btn-default' %>
254
+ </div>
255
+ <% end %>
256
+ </li>
257
+ <li>
258
+ <div class="query-label">
259
+ Current Analytics query:&nbsp;<%= link_to '<i class="fa fa-question-circle"></i>'.html_safe, 'http://docs.killbill.io/latest/userguide_analytics.html#_dashboard_api', :target => '_blank' %>
260
+ </div>
261
+ <pre><%= params[:name] -%></pre>
262
+ </li>
263
+ <% end %>
264
+ <li><%= link_to 'SQL query', kanaui_engine.reports_path(params.to_h.merge(:sql_only => true)) %></li>
265
+ </ul>
266
+ </div>
267
+ </div>
268
+ <% end %>
269
+ </div>
270
+ </div>
172
271
  </div>
173
272
  </div>
174
- <% end %>
273
+
274
+ </div>
175
275
  </div>
176
276
  </div>
177
277
  </div>
178
278
  </div>
179
279
 
180
-
181
280
  <%= javascript_tag do %>
182
- $(document).ready(function() {
183
- $('.calendar-icon').click(function() {
184
- $('.form-container').toggle();
185
- });
186
- });
187
- <% end %>
188
-
189
- <style type="text/css">
190
- .form-container {
191
- display: none;
281
+ function goToReport(url) {
282
+ if (!url) {
283
+ return;
284
+ }
285
+
286
+ // Remove leading/trailing whitespace
287
+ url = url.trim();
288
+
289
+ // Only allow relative URLs (starting with '/')
290
+ // Reject absolute URLs (starting with http://, https://, //, etc.)
291
+ // Reject dangerous schemes (javascript:, data:, vbscript:, etc.)
292
+ if (!url.match(/^\/[^\/]/) && url !== '/') {
293
+ console.error('Invalid URL: only relative paths starting with "/" are allowed');
294
+ return;
295
+ }
296
+
297
+ // Additional check: reject any URL containing dangerous schemes
298
+ var lowerUrl = url.toLowerCase();
299
+ if (lowerUrl.indexOf('javascript:') !== -1 ||
300
+ lowerUrl.indexOf('data:') !== -1 ||
301
+ lowerUrl.indexOf('vbscript:') !== -1 ||
302
+ lowerUrl.indexOf('file:') !== -1) {
303
+ console.error('Invalid URL: dangerous scheme detected');
304
+ return;
305
+ }
306
+
307
+ // Reject URLs starting with // (protocol-relative URLs)
308
+ if (url.indexOf('//') === 0) {
309
+ console.error('Invalid URL: protocol-relative URLs are not allowed');
310
+ return;
311
+ }
312
+
313
+ // Safe to navigate
314
+ window.location.href = url;
192
315
  }
193
- </style>
316
+ <% end %>