kanaui 5.0.0 → 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/kanaui/kiddo/axes.js +5 -7
- data/app/assets/javascripts/kanaui/kiddo/charts/line_chart.js +41 -52
- data/app/assets/javascripts/kanaui/kiddo/charts/pie_chart.js +18 -13
- data/app/assets/javascripts/kanaui/kiddo/charts/utils/mouse_over.js +23 -13
- data/app/assets/javascripts/kanaui/kiddo/helper.js +91 -4
- data/app/assets/javascripts/kanaui/kiddo/kiddo_initialize.js +29 -6
- data/app/assets/javascripts/kanaui/kiddo/settings.js +1 -0
- data/app/assets/stylesheets/kanaui/kanaui.css +103 -18
- data/app/assets/stylesheets/kanaui/reports.css +85 -0
- data/app/controllers/kanaui/dashboard_controller.rb +20 -17
- data/app/controllers/kanaui/engine_controller.rb +21 -2
- data/app/controllers/kanaui/reports_controller.rb +22 -13
- data/app/helpers/kanaui/dashboard_helper.rb +9 -3
- data/app/views/kanaui/dashboard/index.html.erb +48 -17
- data/app/views/kanaui/layouts/kanaui_application.html.erb +11 -15
- data/app/views/kanaui/reports/_form.html.erb +124 -23
- data/app/views/kanaui/reports/_reports_table.html.erb +22 -17
- data/app/views/kanaui/reports/edit.html.erb +1 -1
- data/app/views/kanaui/reports/index.html.erb +6 -3
- data/app/views/kanaui/reports/new.html.erb +1 -1
- data/config/locales/en.bootstrap.yml +64 -0
- data/config/routes.rb +3 -3
- data/lib/kanaui/engine.rb +0 -1
- data/lib/kanaui/version.rb +1 -1
- data/vendor/assets/javascripts/d3.js +2 -0
- data/vendor/assets/javascripts/log4javascript.js +274 -0
- metadata +4 -16
|
@@ -103,11 +103,40 @@
|
|
|
103
103
|
|
|
104
104
|
.kenui-analytics-dashboard-index .well ul {
|
|
105
105
|
list-style-type: none;
|
|
106
|
-
margin
|
|
106
|
+
margin: 0.625rem 0 0;
|
|
107
107
|
background: #fafafa;
|
|
108
|
-
width:
|
|
109
|
-
padding:
|
|
108
|
+
width: 100%;
|
|
109
|
+
padding: 1rem 1.25rem;
|
|
110
110
|
border-radius: 0.5rem;
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-wrap: wrap;
|
|
113
|
+
gap: 0.5rem 1.25rem;
|
|
114
|
+
align-items: flex-start;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Smoothing option links (Weekly/Monthly average/sum) render as inline pills */
|
|
118
|
+
.kenui-analytics-dashboard-index .well > ul > li.smoothing-option {
|
|
119
|
+
display: inline-flex;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.kenui-analytics-dashboard-index .well > ul > li.smoothing-option > a {
|
|
123
|
+
display: inline-flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
background: #ffffff;
|
|
126
|
+
border: 0.0625rem solid #d5d7da;
|
|
127
|
+
border-radius: 999px;
|
|
128
|
+
padding: 0.375rem 0.75rem;
|
|
129
|
+
text-decoration: none;
|
|
130
|
+
color: #414651;
|
|
131
|
+
font-weight: 500;
|
|
132
|
+
font-size: 0.8125rem;
|
|
133
|
+
line-height: 1.25rem;
|
|
134
|
+
transition: all 0.15s ease-in-out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.kenui-analytics-dashboard-index .well > ul > li.smoothing-option > a:hover {
|
|
138
|
+
border-color: #1570ef;
|
|
139
|
+
color: #1570ef;
|
|
111
140
|
}
|
|
112
141
|
|
|
113
142
|
.kenui-analytics-dashboard-index .well ul li a {
|
|
@@ -168,6 +197,26 @@
|
|
|
168
197
|
background: #ffffff;
|
|
169
198
|
}
|
|
170
199
|
|
|
200
|
+
.kenui-analytics-dashboard-index .chart-title-container {
|
|
201
|
+
width: auto;
|
|
202
|
+
height: auto;
|
|
203
|
+
display: block;
|
|
204
|
+
justify-content: unset;
|
|
205
|
+
align-items: unset;
|
|
206
|
+
position: static;
|
|
207
|
+
margin-top: 15px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.kenui-analytics-dashboard-index .chart-title {
|
|
211
|
+
font-weight: 600;
|
|
212
|
+
font-size: 1.25rem;
|
|
213
|
+
line-height: 1.75rem;
|
|
214
|
+
color: #111827;
|
|
215
|
+
white-space: normal;
|
|
216
|
+
transform: none;
|
|
217
|
+
text-align: left;
|
|
218
|
+
}
|
|
219
|
+
|
|
171
220
|
.kenui-analytics-dashboard-index #chartAnchor svg {
|
|
172
221
|
background: #ffffff;
|
|
173
222
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
@@ -329,11 +378,25 @@
|
|
|
329
378
|
|
|
330
379
|
/* app/views/kanaui/reports/index.html.erb */
|
|
331
380
|
|
|
381
|
+
.kanaui-flash-container {
|
|
382
|
+
margin: 1rem 0 1.5rem;
|
|
383
|
+
position: relative;
|
|
384
|
+
z-index: 1;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.kanaui-flash-container .alert {
|
|
388
|
+
margin-bottom: 0.75rem;
|
|
389
|
+
}
|
|
390
|
+
|
|
332
391
|
.kanaui-reports-index .configured-reports {
|
|
333
392
|
max-width: 80rem;
|
|
334
393
|
width: 100%;
|
|
335
394
|
}
|
|
336
395
|
|
|
396
|
+
.kanaui-reports-index .kanaui-report-notice {
|
|
397
|
+
margin-bottom: 1rem;
|
|
398
|
+
}
|
|
399
|
+
|
|
337
400
|
.kanaui-reports-index .configured-reports .configured-reports-header {
|
|
338
401
|
display: flex;
|
|
339
402
|
align-items: start;
|
|
@@ -393,6 +456,11 @@
|
|
|
393
456
|
color: #d92d20 !important;
|
|
394
457
|
}
|
|
395
458
|
|
|
459
|
+
.kanaui-reports-index .disabled-table-button {
|
|
460
|
+
color: #98a2b3 !important;
|
|
461
|
+
cursor: not-allowed;
|
|
462
|
+
}
|
|
463
|
+
|
|
396
464
|
.kanaui-reports-index .edit-button {
|
|
397
465
|
color: #535862 !important;
|
|
398
466
|
}
|
|
@@ -600,20 +668,32 @@
|
|
|
600
668
|
.kenui-analytics-dashboard-index .advanced-controls {
|
|
601
669
|
margin: 0.625rem 0;
|
|
602
670
|
padding: 0;
|
|
671
|
+
flex: 1 1 100%;
|
|
603
672
|
}
|
|
604
673
|
|
|
605
674
|
.kenui-analytics-dashboard-index .advanced-controls .form-horizontal {
|
|
606
|
-
background: #
|
|
675
|
+
background: #ffffff;
|
|
607
676
|
border: 0.0625rem solid #e9eaeb;
|
|
608
677
|
border-radius: 0.5rem;
|
|
609
|
-
padding:
|
|
678
|
+
padding: 1rem 1.25rem;
|
|
610
679
|
margin-top: 0.625rem;
|
|
680
|
+
display: grid;
|
|
681
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
682
|
+
gap: 1rem 1.5rem;
|
|
683
|
+
align-items: start;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.kenui-analytics-dashboard-index .advanced-controls .form-horizontal > .form-group:last-of-type {
|
|
687
|
+
grid-column: 1 / -1;
|
|
688
|
+
justify-self: end;
|
|
689
|
+
margin-top: 0;
|
|
611
690
|
}
|
|
612
691
|
|
|
613
692
|
.kenui-analytics-dashboard-index .advanced-controls fieldset {
|
|
614
693
|
border: none;
|
|
615
694
|
margin: 0;
|
|
616
695
|
padding: 0;
|
|
696
|
+
min-width: 0;
|
|
617
697
|
}
|
|
618
698
|
|
|
619
699
|
.kenui-analytics-dashboard-index .advanced-controls legend {
|
|
@@ -628,10 +708,10 @@
|
|
|
628
708
|
}
|
|
629
709
|
|
|
630
710
|
.kenui-analytics-dashboard-index .advanced-controls .form-group {
|
|
631
|
-
margin-bottom:
|
|
711
|
+
margin-bottom: 0.75rem;
|
|
632
712
|
display: flex;
|
|
633
713
|
flex-direction: column;
|
|
634
|
-
align-items:
|
|
714
|
+
align-items: stretch;
|
|
635
715
|
}
|
|
636
716
|
|
|
637
717
|
.kenui-analytics-dashboard-index .advanced-controls .control-label {
|
|
@@ -656,7 +736,7 @@
|
|
|
656
736
|
|
|
657
737
|
.kenui-analytics-dashboard-index .advanced-controls select.form-control {
|
|
658
738
|
height: 6rem;
|
|
659
|
-
min-width:
|
|
739
|
+
min-width: 0;
|
|
660
740
|
width: 100%;
|
|
661
741
|
}
|
|
662
742
|
|
|
@@ -696,18 +776,19 @@
|
|
|
696
776
|
}
|
|
697
777
|
|
|
698
778
|
/* Current Analytics Query Section */
|
|
699
|
-
.kenui-analytics-dashboard-index .well ul li
|
|
700
|
-
background: #
|
|
779
|
+
.kenui-analytics-dashboard-index .well ul li.query-block {
|
|
780
|
+
background: #ffffff;
|
|
701
781
|
border: 0.0625rem solid #e9eaeb;
|
|
702
782
|
border-radius: 0.5rem;
|
|
703
|
-
padding: 0.
|
|
704
|
-
margin: 0
|
|
783
|
+
padding: 0.75rem 1rem;
|
|
784
|
+
margin: 0;
|
|
705
785
|
display: flex;
|
|
706
786
|
flex-direction: column;
|
|
707
|
-
gap: 0.
|
|
787
|
+
gap: 0.5rem;
|
|
788
|
+
flex: 1 1 100%;
|
|
708
789
|
}
|
|
709
790
|
|
|
710
|
-
.kenui-analytics-dashboard-index .well ul li
|
|
791
|
+
.kenui-analytics-dashboard-index .well ul li.query-block a {
|
|
711
792
|
color: #1570ef;
|
|
712
793
|
text-decoration: none;
|
|
713
794
|
font-weight: 500;
|
|
@@ -720,12 +801,12 @@
|
|
|
720
801
|
transition: color 0.15s ease-in-out;
|
|
721
802
|
}
|
|
722
803
|
|
|
723
|
-
.kenui-analytics-dashboard-index .well ul li
|
|
804
|
+
.kenui-analytics-dashboard-index .well ul li.query-block a:hover {
|
|
724
805
|
color: #0d5bb8;
|
|
725
806
|
text-decoration: underline;
|
|
726
807
|
}
|
|
727
808
|
|
|
728
|
-
.kenui-analytics-dashboard-index .well ul li
|
|
809
|
+
.kenui-analytics-dashboard-index .well ul li.query-block pre {
|
|
729
810
|
background: #ffffff;
|
|
730
811
|
border: 0.0625rem solid #d5d7da;
|
|
731
812
|
border-radius: 0.375rem;
|
|
@@ -741,7 +822,7 @@
|
|
|
741
822
|
box-shadow: 0 0.0625rem 0.1875rem rgba(0, 0, 0, 0.1);
|
|
742
823
|
}
|
|
743
824
|
|
|
744
|
-
.kenui-analytics-dashboard-index .well ul li
|
|
825
|
+
.kenui-analytics-dashboard-index .well ul li.query-block .query-label {
|
|
745
826
|
font-weight: 500;
|
|
746
827
|
font-size: 0.875rem;
|
|
747
828
|
line-height: 1.25rem;
|
|
@@ -751,6 +832,10 @@
|
|
|
751
832
|
|
|
752
833
|
/* Responsive adjustments for smaller screens */
|
|
753
834
|
@media (max-width: 768px) {
|
|
835
|
+
.kenui-analytics-dashboard-index .advanced-controls .form-horizontal {
|
|
836
|
+
grid-template-columns: 1fr;
|
|
837
|
+
}
|
|
838
|
+
|
|
754
839
|
.kenui-analytics-dashboard-index .advanced-controls .form-group {
|
|
755
840
|
flex-direction: column;
|
|
756
841
|
align-items: flex-start;
|
|
@@ -766,7 +851,7 @@
|
|
|
766
851
|
width: 100%;
|
|
767
852
|
}
|
|
768
853
|
|
|
769
|
-
.kenui-analytics-dashboard-index .well ul li
|
|
854
|
+
.kenui-analytics-dashboard-index .well ul li.query-block pre {
|
|
770
855
|
font-size: 0.75rem;
|
|
771
856
|
padding: 0.5rem 0.75rem;
|
|
772
857
|
}
|
|
@@ -3,6 +3,91 @@
|
|
|
3
3
|
white-space: nowrap;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
.kanaui-field-control {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
gap: 8px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.kanaui-field-control-top {
|
|
13
|
+
align-items: flex-start;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.kanaui-field-control .form-control {
|
|
17
|
+
flex: 1 1 auto;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.kanaui-field-help {
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
flex: 0 0 auto;
|
|
25
|
+
width: 22px;
|
|
26
|
+
height: 22px;
|
|
27
|
+
padding: 0;
|
|
28
|
+
color: #6b7280;
|
|
29
|
+
background: transparent;
|
|
30
|
+
border: 0;
|
|
31
|
+
box-shadow: none;
|
|
32
|
+
font-size: 16px;
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
line-height: 1;
|
|
35
|
+
text-align: center;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.kanaui-field-help:hover,
|
|
40
|
+
.kanaui-field-help:focus {
|
|
41
|
+
color: #111827;
|
|
42
|
+
background: transparent;
|
|
43
|
+
outline: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.kanaui-field-help:focus {
|
|
47
|
+
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.24);
|
|
48
|
+
border-radius: 3px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.kanaui-field-tooltip {
|
|
52
|
+
position: absolute;
|
|
53
|
+
z-index: 1070;
|
|
54
|
+
display: none;
|
|
55
|
+
max-width: 360px;
|
|
56
|
+
padding: 10px 12px;
|
|
57
|
+
color: #1f2937;
|
|
58
|
+
background-color: #fff;
|
|
59
|
+
border: 1px solid #d1d5db;
|
|
60
|
+
border-radius: 6px;
|
|
61
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
|
|
62
|
+
font-size: 14px;
|
|
63
|
+
font-weight: 400;
|
|
64
|
+
line-height: 1.5;
|
|
65
|
+
pointer-events: none;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.kanaui-field-tooltip::before {
|
|
69
|
+
content: "";
|
|
70
|
+
position: absolute;
|
|
71
|
+
top: 12px;
|
|
72
|
+
left: -6px;
|
|
73
|
+
width: 0;
|
|
74
|
+
height: 0;
|
|
75
|
+
border-top: 6px solid transparent;
|
|
76
|
+
border-bottom: 6px solid transparent;
|
|
77
|
+
border-right: 6px solid #fff;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.kanaui-field-tooltip.is-flipped::before {
|
|
81
|
+
left: auto;
|
|
82
|
+
right: -6px;
|
|
83
|
+
border-right: 0;
|
|
84
|
+
border-left: 6px solid #fff;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.kanaui-field-tooltip.is-visible {
|
|
88
|
+
display: block;
|
|
89
|
+
}
|
|
90
|
+
|
|
6
91
|
.sql-query {
|
|
7
92
|
word-break: break-word;
|
|
8
93
|
}
|
|
@@ -11,7 +11,7 @@ module Kanaui
|
|
|
11
11
|
|
|
12
12
|
@raw_name = (params[:name] || '').split('^')[0]
|
|
13
13
|
|
|
14
|
-
@end_date = params[:end_date] ||
|
|
14
|
+
@end_date = params[:end_date] || Time.zone.today.to_s
|
|
15
15
|
|
|
16
16
|
@available_start_dates = start_date_options
|
|
17
17
|
@start_date = params[:start_date] || (params[:delta_days].present? ? (@end_date.to_date - params[:delta_days].to_i.day).to_s : @available_start_dates['Last 6 months'])
|
|
@@ -19,9 +19,13 @@ 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
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
# If no report name is provided, redirect to a default report.
|
|
23
|
+
# Prefer the historical default (second report) when present, but fall back
|
|
24
|
+
# to the first report so a single configured report still renders the
|
|
25
|
+
# dashboard controls and chart area.
|
|
26
|
+
default_report = @reports[1] || @reports[0] if @reports.is_a?(Array)
|
|
27
|
+
default_name = default_report&.fetch('reportName', nil).presence
|
|
28
|
+
if @raw_name.blank? && default_name.present?
|
|
25
29
|
query_params = { start_date: @start_date,
|
|
26
30
|
end_date: @end_date,
|
|
27
31
|
name: default_name,
|
|
@@ -29,8 +33,8 @@ module Kanaui
|
|
|
29
33
|
sql_only: params[:sql_only],
|
|
30
34
|
format: params[:format] }
|
|
31
35
|
|
|
32
|
-
query_params[:fake] = params[:fake]
|
|
33
|
-
query_params[:type] = params[:type]
|
|
36
|
+
query_params[:fake] = params[:fake] if params[:fake].present?
|
|
37
|
+
query_params[:type] = params[:type] if params[:type].present?
|
|
34
38
|
|
|
35
39
|
redirect_to dashboard_index_path(query_params) and return
|
|
36
40
|
end
|
|
@@ -46,14 +50,14 @@ module Kanaui
|
|
|
46
50
|
name = query.present? ? "#{params[:name]}#{query}^metric:count" : params[:name]
|
|
47
51
|
query_params = { start_date: @start_date,
|
|
48
52
|
end_date: @end_date,
|
|
49
|
-
name
|
|
53
|
+
name:,
|
|
50
54
|
smooth: params[:smooth],
|
|
51
55
|
sql_only: params[:sql_only],
|
|
52
56
|
format: params[:format] }
|
|
53
57
|
|
|
54
58
|
# Test only
|
|
55
|
-
query_params[:fake] = params[:fake]
|
|
56
|
-
query_params[:type] = params[:type]
|
|
59
|
+
query_params[:fake] = params[:fake] if params[:fake].present?
|
|
60
|
+
query_params[:type] = params[:type] if params[:type].present?
|
|
57
61
|
|
|
58
62
|
redirect_to dashboard_index_path(query_params) and return
|
|
59
63
|
end
|
|
@@ -86,12 +90,12 @@ module Kanaui
|
|
|
86
90
|
respond_to do |fmt|
|
|
87
91
|
fmt.csv do
|
|
88
92
|
filename = params[:name]
|
|
89
|
-
|
|
93
|
+
if params[:start_date].present?
|
|
90
94
|
filename += "_#{params[:start_date]}"
|
|
91
|
-
filename += "-#{params[:end_date]}"
|
|
95
|
+
filename += "-#{params[:end_date]}" if params[:end_date].present?
|
|
92
96
|
end
|
|
93
97
|
filename += '.csv'
|
|
94
|
-
send_data(raw_reports, filename:
|
|
98
|
+
send_data(raw_reports, filename:)
|
|
95
99
|
end
|
|
96
100
|
fmt.all { render json: reports }
|
|
97
101
|
end
|
|
@@ -142,20 +146,19 @@ module Kanaui
|
|
|
142
146
|
groups[field_name] = params["group_#{field_name}"]
|
|
143
147
|
end
|
|
144
148
|
|
|
145
|
-
filter_query = ''
|
|
149
|
+
filter_query = +''
|
|
146
150
|
filters.each do |k, v|
|
|
147
151
|
next if v.blank?
|
|
148
152
|
|
|
149
|
-
filter_query
|
|
153
|
+
filter_query.presence&.<<('%26')
|
|
150
154
|
filter_query << "(#{k}=#{v.join("|#{k}=")})"
|
|
151
155
|
end
|
|
152
|
-
query << "^filter:#{filter_query}"
|
|
156
|
+
query << "^filter:#{filter_query}" if filter_query.present?
|
|
153
157
|
|
|
154
158
|
groups.each do |k, v|
|
|
155
159
|
next if v.blank?
|
|
156
160
|
|
|
157
|
-
|
|
158
|
-
query << "^dimension:#{k}(#{v.join('|')}|-)"
|
|
161
|
+
query << "^dimension:#{k}(#{v.join('|')})"
|
|
159
162
|
end
|
|
160
163
|
|
|
161
164
|
# Template variables
|
|
@@ -27,8 +27,27 @@ module Kanaui
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
rescue_from(KillBillClient::API::ResponseError) do |killbill_exception|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
error_message = I18n.t('kanaui.errors.killbill_communication', error: as_string(killbill_exception))
|
|
31
|
+
|
|
32
|
+
if json_request?
|
|
33
|
+
render json: { message: error_message }, status: killbill_exception.response.code.to_i
|
|
34
|
+
else
|
|
35
|
+
flash[:error] = error_message
|
|
36
|
+
# Avoid redirect loops when the dashboard itself cannot be loaded.
|
|
37
|
+
if controller_name == 'dashboard' && action_name == 'index'
|
|
38
|
+
render plain: error_message, status: killbill_exception.response.code.to_i
|
|
39
|
+
else
|
|
40
|
+
redirect_to dashboard_index_path
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def json_request?
|
|
46
|
+
request.format.json? || params[:format] == 'json' || dashboard_reports_json_request?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def dashboard_reports_json_request?
|
|
50
|
+
controller_name == 'dashboard' && action_name == 'reports' && params[:format].blank?
|
|
32
51
|
end
|
|
33
52
|
|
|
34
53
|
def as_string(e)
|
|
@@ -4,48 +4,57 @@ module Kanaui
|
|
|
4
4
|
class ReportsController < Kanaui::EngineController
|
|
5
5
|
def index
|
|
6
6
|
@reports = JSON.parse(Kanaui::DashboardHelper::DashboardApi.available_reports(options_for_klient)).map(&:deep_symbolize_keys)
|
|
7
|
+
@report_notice = report_notice_from_flash
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
def new
|
|
10
11
|
@report = {}
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
def create
|
|
14
|
-
Kanaui::DashboardHelper::DashboardApi.create_report(report_from_params.to_json, options_for_klient)
|
|
15
|
-
|
|
16
|
-
flash[:notice] = 'Report successfully created'
|
|
17
|
-
redirect_to action: :index
|
|
18
|
-
end
|
|
19
|
-
|
|
20
14
|
def edit
|
|
21
15
|
@report = JSON.parse(Kanaui::DashboardHelper::DashboardApi.available_reports(options_for_klient))
|
|
22
16
|
.find { |x| x['reportName'] == params.require(:id) }
|
|
23
17
|
.deep_symbolize_keys
|
|
24
18
|
end
|
|
25
19
|
|
|
20
|
+
def create
|
|
21
|
+
Kanaui::DashboardHelper::DashboardApi.create_report(report_from_params.to_json, options_for_klient)
|
|
22
|
+
|
|
23
|
+
redirect_to_index_with_notice(:created)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
26
|
def update
|
|
27
27
|
Kanaui::DashboardHelper::DashboardApi.update_report(params.require(:id), report_from_params.to_json, options_for_klient)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
redirect_to action: :index
|
|
29
|
+
redirect_to_index_with_notice(:updated)
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
def refresh
|
|
34
33
|
Kanaui::DashboardHelper::DashboardApi.refresh_report(params.require(:id), options_for_klient)
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
redirect_to action: :index
|
|
35
|
+
redirect_to_index_with_notice(:refresh_scheduled)
|
|
38
36
|
end
|
|
39
37
|
|
|
40
38
|
def destroy
|
|
41
39
|
Kanaui::DashboardHelper::DashboardApi.delete_report(params.require(:id), options_for_klient)
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
redirect_to action: :index
|
|
41
|
+
redirect_to_index_with_notice(:deleted)
|
|
45
42
|
end
|
|
46
43
|
|
|
47
44
|
private
|
|
48
45
|
|
|
46
|
+
def report_notice_from_flash
|
|
47
|
+
notice_key = flash[:report_notice].presence&.to_s
|
|
48
|
+
return nil unless %w[created updated refresh_scheduled deleted].include?(notice_key)
|
|
49
|
+
|
|
50
|
+
I18n.t("kanaui.reports.notices.#{notice_key}", default: nil)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def redirect_to_index_with_notice(notice_key)
|
|
54
|
+
flash[:report_notice] = notice_key
|
|
55
|
+
redirect_to action: :index
|
|
56
|
+
end
|
|
57
|
+
|
|
49
58
|
def report_from_params
|
|
50
59
|
{
|
|
51
60
|
reportName: params[:report_name],
|
|
@@ -18,9 +18,15 @@ module Kanaui
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def reports(start_date, end_date, name, smooth, sql_only, format, options = {})
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
query = {
|
|
22
|
+
'format' => format,
|
|
23
|
+
'startDate' => start_date,
|
|
24
|
+
'endDate' => end_date,
|
|
25
|
+
'name' => name
|
|
26
|
+
}
|
|
27
|
+
query['smooth'] = smooth if smooth
|
|
28
|
+
query['sqlOnly'] = 'true' if sql_only.present?
|
|
29
|
+
path = "#{KILLBILL_ANALYTICS_PREFIX}/reports?#{query.to_query}"
|
|
24
30
|
response = KillBillClient::API.get path, {}, options
|
|
25
31
|
response.body
|
|
26
32
|
end
|
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
<%= link_to "Fake line", kanaui_engine.dashboard_index_path(fake: 1, name: 'fake_line', type: 'line') %>
|
|
44
44
|
</li>
|
|
45
45
|
</ul>
|
|
46
|
+
<% elsif @reports.empty? %>
|
|
47
|
+
<select class="form-select" id="report-select" disabled>
|
|
48
|
+
<option value=""><%= t('kanaui.dashboard.no_reports_available') %></option>
|
|
49
|
+
</select>
|
|
46
50
|
<% else %>
|
|
47
51
|
<select class="form-select" id="report-select" onchange="goToReport(this.value);">
|
|
48
52
|
<% @reports.each_with_index do |r, idx| %>
|
|
@@ -58,7 +62,7 @@
|
|
|
58
62
|
|
|
59
63
|
|
|
60
64
|
<div class="calendar-container">
|
|
61
|
-
<% if params[:name] %>
|
|
65
|
+
<% if params[:name].present? || @reports.empty? %>
|
|
62
66
|
<div class="form-container d-flex">
|
|
63
67
|
<%= form_tag kanaui_engine.dashboard_index_path, :class => 'form-vertical d-flex', :method => :get do %>
|
|
64
68
|
<div class="trigger-invoice-box form-group">
|
|
@@ -79,7 +83,7 @@
|
|
|
79
83
|
<path d="M2.83325 8.33325H17.8333" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
80
84
|
</svg>
|
|
81
85
|
|
|
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 %>"
|
|
86
|
+
<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 %>"<%= ' disabled' if @reports.empty? %>>
|
|
83
87
|
</div>
|
|
84
88
|
</div>
|
|
85
89
|
<div class="form-group d-flex align-items-center gap-2">
|
|
@@ -92,7 +96,7 @@
|
|
|
92
96
|
<path d="M2.83325 8.33325H17.8333" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
93
97
|
</svg>
|
|
94
98
|
|
|
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 %>"
|
|
99
|
+
<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 %>"<%= ' disabled' if @reports.empty? %>>
|
|
96
100
|
</div>
|
|
97
101
|
</div>
|
|
98
102
|
<% ((@report['variables'] || {})['fields'] || []).each do |definition| %>
|
|
@@ -118,6 +122,7 @@
|
|
|
118
122
|
variant: "outline-secondary d-inline-flex align-items-center gap-1",
|
|
119
123
|
type: "submit",
|
|
120
124
|
html_class: "btn btn-xs kaui-dropdown custom-hover py-2 px-3 refresh-button",
|
|
125
|
+
html_options: { disabled: @reports.empty? },
|
|
121
126
|
} %>
|
|
122
127
|
|
|
123
128
|
<%= link_to kanaui_engine.url_for(:controller => :reports), class: 'text-decoration-none' do %>
|
|
@@ -145,6 +150,11 @@
|
|
|
145
150
|
<div class="">
|
|
146
151
|
<% if params[:name] %>
|
|
147
152
|
|
|
153
|
+
<div class="chart-title-container mb-2">
|
|
154
|
+
<h2 class="chart-title" id="chart-title">
|
|
155
|
+
<%= @report['reportPrettyName'] %>
|
|
156
|
+
</h2>
|
|
157
|
+
</div>
|
|
148
158
|
<div id="date-controls" style="display: none;">
|
|
149
159
|
<div class="custom-tabs my-4">
|
|
150
160
|
<% @available_start_dates.each do |key, value| %>
|
|
@@ -153,14 +163,7 @@
|
|
|
153
163
|
</div>
|
|
154
164
|
</div>
|
|
155
165
|
<div id="loading-spinner"></div>
|
|
156
|
-
<div
|
|
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>
|
|
166
|
+
<div id="chartAnchor" data-reports-path="<%= kanaui_engine.reports_path(params.to_h) %>" data-report-name="<%= @report['reportPrettyName'] %>" class="w-100"></div>
|
|
164
167
|
|
|
165
168
|
<hr>
|
|
166
169
|
|
|
@@ -202,16 +205,16 @@
|
|
|
202
205
|
<% 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
206
|
<% 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
207
|
<% 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>
|
|
208
|
+
<li class="smoothing-option"><%= link_to 'Weekly average', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'AVERAGE_WEEKLY')) %></li>
|
|
206
209
|
<% end %>
|
|
207
210
|
<% 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>
|
|
211
|
+
<li class="smoothing-option"><%= link_to 'Monthly average', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'AVERAGE_MONTHLY')) %></li>
|
|
209
212
|
<% end %>
|
|
210
213
|
<% 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>
|
|
214
|
+
<li class="smoothing-option"><%= link_to 'Weekly sum', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'SUM_WEEKLY')) %></li>
|
|
212
215
|
<% end %>
|
|
213
216
|
<% 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>
|
|
217
|
+
<li class="smoothing-option"><%= link_to 'Monthly sum', kanaui_engine.dashboard_index_path(params.to_h.merge(:smooth => 'SUM_MONTHLY')) %></li>
|
|
215
218
|
<% end %>
|
|
216
219
|
<% end %>
|
|
217
220
|
<% filter_fields = ((@report['schema'] || {})['fields'] || []).select { |field| !field['distinctValues'].blank? && field['dataType'] =~ /char/ } # To ignore tenant_record_id %>
|
|
@@ -254,14 +257,14 @@
|
|
|
254
257
|
</div>
|
|
255
258
|
<% end %>
|
|
256
259
|
</li>
|
|
257
|
-
<li>
|
|
260
|
+
<li class="query-block">
|
|
258
261
|
<div class="query-label">
|
|
259
262
|
Current Analytics query: <%= link_to '<i class="fa fa-question-circle"></i>'.html_safe, 'http://docs.killbill.io/latest/userguide_analytics.html#_dashboard_api', :target => '_blank' %>
|
|
260
263
|
</div>
|
|
261
264
|
<pre><%= params[:name] -%></pre>
|
|
262
265
|
</li>
|
|
263
266
|
<% end %>
|
|
264
|
-
<li><%= link_to '
|
|
267
|
+
<li class="smoothing-option"><%= link_to t('kanaui.dashboard.advanced_controls.sql_query'), kanaui_engine.reports_path(params.to_h.merge(:sql_only => true)) %></li>
|
|
265
268
|
</ul>
|
|
266
269
|
</div>
|
|
267
270
|
</div>
|
|
@@ -313,4 +316,32 @@
|
|
|
313
316
|
// Safe to navigate
|
|
314
317
|
window.location.href = url;
|
|
315
318
|
}
|
|
319
|
+
|
|
320
|
+
// Persist the open/closed state of #advanced-controls across page reloads
|
|
321
|
+
(function () {
|
|
322
|
+
var STORAGE_KEY = 'kanaui.advancedControls.open';
|
|
323
|
+
var panel = document.getElementById('advanced-controls');
|
|
324
|
+
if (!panel) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
var toggle = document.querySelector('[data-bs-toggle="collapse"][href="#advanced-controls"]');
|
|
328
|
+
|
|
329
|
+
// Restore previous state before Bootstrap initializes
|
|
330
|
+
try {
|
|
331
|
+
if (localStorage.getItem(STORAGE_KEY) === '1') {
|
|
332
|
+
panel.classList.add('show');
|
|
333
|
+
if (toggle) {
|
|
334
|
+
toggle.setAttribute('aria-expanded', 'true');
|
|
335
|
+
toggle.classList.remove('collapsed');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
} catch (e) { /* localStorage may be unavailable */ }
|
|
339
|
+
|
|
340
|
+
panel.addEventListener('shown.bs.collapse', function () {
|
|
341
|
+
try { localStorage.setItem(STORAGE_KEY, '1'); } catch (e) {}
|
|
342
|
+
});
|
|
343
|
+
panel.addEventListener('hidden.bs.collapse', function () {
|
|
344
|
+
try { localStorage.setItem(STORAGE_KEY, '0'); } catch (e) {}
|
|
345
|
+
});
|
|
346
|
+
})();
|
|
316
347
|
<% end %>
|