kaui 4.0.4 → 4.0.6

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: 44468e809ef2b1ffd8115e0b54664822a212fc2ec26ae6513d8b64135f6de48f
4
- data.tar.gz: 7ceff36da85b32040813f3101246588d5c9cf07a7893297ac1df44d0462b25b2
3
+ metadata.gz: e7e6e1d8f1969609b1eb958efa90c3f1afed414dbb9a754e88e9624716997eec
4
+ data.tar.gz: f42f2106b4c9f9687b267b2d9475820d390aa6a78cd20605341853ada294981b
5
5
  SHA512:
6
- metadata.gz: b0949ac7f3c9054757e5b7d5ea33867aba33738d185071917127032ac10293fc845b2da1dec911ca7d02ce0a8500ee7c95309264b3b05936e0eaf59e7e84d5c8
7
- data.tar.gz: 5d6fcb261e749b7cc6d7c97782b3a78cf917acca0369c055515c18f34f514d739e04062755e669f859be2538d09da2a65d3f5fa1b6ab5a4f8c3e10b39f77a308
6
+ metadata.gz: 314ac1bb0b060b413aa68ec04183eb39f8df63b94fbe68b1c938b1e726e19733f45986278227f5101884aca3e37ec80d7e86b58348c098350138277e7529de1e
7
+ data.tar.gz: 45d12ccbc5d09cc0c77450e5b5dce0476be99b658078123148c6335854e9c3690e834fa7df800de0b91bd4623c96b0603e68ba8a8826a91347ec1973d74e00cb
@@ -439,7 +439,7 @@ function setObjectIdPopover(){
439
439
  // Custom tooltip function for object IDs
440
440
  // attributes:
441
441
  // data-id = content of the tooltip, object id; required
442
- // title = title of the tooltip; not required
442
+ // data-title = title of the tooltip; not required
443
443
  function setObjectIdTooltip() {
444
444
  // Remove any existing tooltips
445
445
  $(".custom-tooltip").remove();
@@ -476,6 +476,9 @@ function showCustomTooltip(element, objectId) {
476
476
  var position = $element.offset();
477
477
  var elementHeight = $element.outerHeight();
478
478
  var elementWidth = $element.outerWidth();
479
+
480
+ // Get custom title from data-title attribute or default to "Subscription ID"
481
+ var tooltipTitle = $element.data("title") || "Subscription ID";
479
482
 
480
483
  // Create tooltip content with header and custom SVG icon
481
484
  var svgIcon =
@@ -485,7 +488,7 @@ function showCustomTooltip(element, objectId) {
485
488
  "</svg>";
486
489
 
487
490
  var tooltipContent =
488
- '<div class="tooltip-header">Subscription ID</div>' +
491
+ '<div class="tooltip-header">' + tooltipTitle + '</div>' +
489
492
  '<div class="tooltip-content">' +
490
493
  '<span class="tooltip-id">' +
491
494
  objectId +
@@ -6,6 +6,10 @@ module Kaui
6
6
 
7
7
  def index
8
8
  @allowed_users = retrieve_allowed_users_for_current_user
9
+ @roles_by_user = {}
10
+ @allowed_users.each do |user|
11
+ @roles_by_user[user.kb_username] = roles_for_user(user)
12
+ end
9
13
  end
10
14
 
11
15
  def new
@@ -14,7 +14,7 @@ module Kaui
14
14
  @max_records = {}
15
15
  %w[account payment invoice].each do |type|
16
16
  model = "Kaui::#{type.capitalize}".constantize
17
- @max_records[type.to_sym] = model.list_or_search(nil, 0, 0, options_for_klient).pagination_max_nb_records || 0
17
+ @max_records[type.to_sym] = model.list_or_search(nil, 0, 0, options_for_klient).pagination_max_nb_records
18
18
  end
19
19
  end
20
20
 
@@ -36,6 +36,15 @@ module Kaui
36
36
  Kaui::Invoice.list_or_search(query_string, 0, MAXIMUM_NUMBER_OF_RECORDS_DOWNLOAD, options_for_klient.merge(params: kb_params))
37
37
  end
38
38
 
39
+ if start_date && end_date
40
+ invoices = invoices.select do |invoice|
41
+ start_date_parsed = Date.parse(start_date)
42
+ end_date_parsed = Date.parse(end_date)
43
+ invoice_date = Date.parse(invoice.invoice_date)
44
+ invoice_date.between?(start_date_parsed, end_date_parsed)
45
+ end
46
+ end
47
+
39
48
  csv_string = CSV.generate(headers: true) do |csv|
40
49
  csv << columns
41
50
 
@@ -24,6 +24,7 @@ module Kaui
24
24
 
25
25
  if all_fields_checked
26
26
  columns = KillBillClient::Model::PaymentAttributes.instance_variable_get('@json_attributes') - %w[transactions audit_logs]
27
+ columns += %w[payment_date status] # additional fields not part of the model
27
28
  csv_headers = columns.dup
28
29
  Kaui::Payment::REMAPPING_FIELDS.each do |k, v|
29
30
  index = csv_headers.index(k)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kaui
4
+ module AdminAllowedUsersHelper
5
+ # Check if a user can be deleted
6
+ # Returns false if the user is an admin or the current logged-in user
7
+ def can_delete_user?(user, user_roles)
8
+ is_admin = user_roles.include?('admin') || user.kb_username == Kaui.root_username
9
+ is_current_user = user.kb_username == current_user.kb_username
10
+
11
+ !is_admin && !is_current_user
12
+ end
13
+ end
14
+ end
@@ -24,6 +24,8 @@ module Kaui
24
24
  '/kenui'
25
25
  when /payment-test/
26
26
  '/payment_test'
27
+ when /aviate/
28
+ '/aviate'
27
29
  else
28
30
  "/#{plugin_key}"
29
31
  end
@@ -43,7 +43,7 @@
43
43
  <path d="M2.5 8.3335H17.5" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
44
44
  </svg>
45
45
  </label>
46
- <input type="text" class="form-control center-input" id="startDate" placeholder="All Accounts" name="startDate">
46
+ <input type="text" class="form-control center-input" id="startDate" placeholder="All Account Timelines" name="startDate">
47
47
  <div id="dash" class="dash">–</div>
48
48
  <input type="text" class="form-control right-input" id="endDate" placeholder="" name="endDate">
49
49
  <div id="toggle-wrapper" class="position-absolute" style="width: 100%; height: 100%;" onclick="document.getElementById('single-toggle').click()"></div>
@@ -117,12 +117,10 @@
117
117
  <% end %>
118
118
  </div>
119
119
 
120
- <% if can? :trigger, Kaui::Invoice %>
121
- <div class="info-item">
122
- <b>Next invoice date</b>
123
- <p id='next-invoice-date'>N/A</p>
124
- </div>
125
- <% end %>
120
+ <div class="info-item">
121
+ <b>Next invoice date</b>
122
+ <p id='next-invoice-date'>N/A</p>
123
+ </div>
126
124
  </div>
127
125
 
128
126
  <!-- Right Column -->
@@ -60,23 +60,16 @@
60
60
  <td><%= link_to u.kb_username, admin_allowed_user_path(u.id) %></td>
61
61
  <td><%= u.description %></td>
62
62
  <td class="text-end">
63
-
64
-
65
- <%= render "kaui/components/button/button", {
66
- label: 'Delete',
67
- variant: "outline-secondary d-inline-flex align-items-center gap-1",
68
- type: "button",
69
- html_class: "kaui-button delete-button custom-hover",
70
- html_options: {
71
- data: {
72
- confirm: "Are you sure?",
73
- method: "delete",
74
- turbo: false,
75
- "url": kaui_engine.admin_allowed_user_path(u.id)
76
- }
77
- }
78
- } %>
79
- <%= link_to kaui_engine.edit_admin_allowed_user_path(u.id), :class => '' do %>
63
+ <% user_roles = @roles_by_user[u.kb_username] || [] %>
64
+ <% if can_delete_user?(u, user_roles) %>
65
+ <%= form_tag kaui_engine.admin_allowed_user_path(u.id), method: :delete, class: 'd-inline', onsubmit: "return confirm('Are you sure?');" do %>
66
+ <%= button_tag type: 'submit', class: 'btn btn-outline-secondary d-inline-flex align-items-center gap-1 kaui-button delete-button custom-hover' do %>
67
+ Delete
68
+ <% end %>
69
+ <% end %>
70
+ <% end %>
71
+
72
+ <%= link_to kaui_engine.edit_admin_allowed_user_path(u.id), class: 'text-decoration-none' do %>
80
73
  <%= render "kaui/components/button/button", {
81
74
  label: "Edit",
82
75
  variant: "outline-secondary d-inline-flex align-items-center gap-1",
@@ -154,6 +147,8 @@
154
147
  $header.find('.sort-' + direction).addClass('active');
155
148
  }
156
149
 
150
+ } catch (e) {
151
+ console.error('Error initializing DataTable:', e);
157
152
  }
158
153
  }, 100);
159
154
  });
@@ -6,21 +6,23 @@
6
6
  <h2>User details</h2>
7
7
  </div>
8
8
  <span>
9
- <%= render "kaui/components/button/button", {
10
- label: 'Delete',
11
- variant: "outline-secondary d-inline-flex align-items-center gap-1",
12
- type: "button",
13
- html_class: "kaui-button delete-button custom-hover",
14
- html_options: {
15
- data: {
16
- confirm: "Are you sure?",
17
- method: "delete",
18
- turbo: false, # Optional: disable Turbo if using Rails Turbo
19
- "url": kaui_engine.admin_allowed_user_path(@allowed_user.id)
20
- },
21
- onclick: "if (confirm(this.dataset.confirm)) { Rails.ajax({ url: this.dataset.url, type: this.dataset.method }); }"
22
- }
23
- } %>
9
+ <% if can_delete_user?(@allowed_user, @roles) %>
10
+ <%= render "kaui/components/button/button", {
11
+ label: 'Delete',
12
+ variant: "outline-secondary d-inline-flex align-items-center gap-1",
13
+ type: "button",
14
+ html_class: "kaui-button delete-button custom-hover",
15
+ html_options: {
16
+ data: {
17
+ confirm: "Are you sure?",
18
+ method: "delete",
19
+ turbo: false, # Optional: disable Turbo if using Rails Turbo
20
+ "url": kaui_engine.admin_allowed_user_path(@allowed_user.id)
21
+ },
22
+ onclick: "if (confirm(this.dataset.confirm)) { Rails.ajax({ url: this.dataset.url, type: this.dataset.method }); }"
23
+ }
24
+ } %>
25
+ <% end %>
24
26
 
25
27
  <%= link_to kaui_engine.edit_admin_allowed_user_path(@allowed_user.id),
26
28
  :class => '' do %>
@@ -246,6 +246,8 @@ function switch_basic_config() {
246
246
  $(document).ready(function() {
247
247
  switch_basic_config();
248
248
 
249
+ // jQuery UI autocomplete interferes with HTML5 validation
250
+ // So we keep the custom JS validation
249
251
  $('#simple_plan_product_name').autocomplete({
250
252
  source: function(query, process) {
251
253
  process(known_products());
@@ -261,8 +263,75 @@ $(document).ready(function() {
261
263
  $('#simple_plan_product_name').on('mouseleave', function() {
262
264
  recompute_available_base_products_for_ao();
263
265
  });
266
+
267
+ // Realtime validation function
268
+ function validateField(field) {
269
+ var pattern = /^[a-zA-Z_][\w\.-]*$/;
270
+ var value = field.val().trim();
271
+ var fieldName = field.attr('id') === 'simple_plan_product_name' ? 'Product Name' : 'Plan Name';
272
+
273
+ if (value === '') {
274
+ showError(field, fieldName + ' is required');
275
+ return false;
276
+ } else if (!pattern.test(value)) {
277
+ showError(field, fieldName + ' must start with a letter or underscore, and can only contain letters, digits, underscores, hyphens, and periods');
278
+ return false;
279
+ } else {
280
+ clearError(field);
281
+ return true;
282
+ }
283
+ }
284
+
285
+ // Realtime validation on input
286
+ $('#simple_plan_product_name, #simple_plan_plan_id').on('input', function() {
287
+ validateField($(this));
288
+ });
289
+
290
+ // Validation on blur (when user leaves the field)
291
+ $('#simple_plan_product_name, #simple_plan_plan_id').on('blur', function() {
292
+ validateField($(this));
293
+ });
294
+
295
+ // Custom validation on form submit
296
+ $('#catalog_simple form').on('submit', function(e) {
297
+ var isValid = true;
298
+
299
+ // Validate Product Name
300
+ if (!validateField($('#simple_plan_product_name'))) {
301
+ isValid = false;
302
+ }
303
+
304
+ // Validate Plan Name
305
+ if (!validateField($('#simple_plan_plan_id'))) {
306
+ isValid = false;
307
+ }
308
+
309
+ if (!isValid) {
310
+ e.preventDefault();
311
+ e.stopPropagation();
312
+ // Scroll to first error
313
+ $('.error-message:first').get(0)?.scrollIntoView({ behavior: 'smooth', block: 'center' });
314
+ return false;
315
+ }
316
+
317
+ return true;
318
+ });
264
319
  });
265
320
 
321
+ function showError(field, message) {
322
+ clearError(field);
323
+ field.addClass('error-field');
324
+ field.css('border-color', '#dc3545');
325
+ var errorDiv = $('<div class="error-message" style="color: #dc3545; font-size: 0.875rem; margin-top: 0.25rem;"></div>').text(message);
326
+ field.parent().append(errorDiv);
327
+ }
328
+
329
+ function clearError(field) {
330
+ field.removeClass('error-field');
331
+ field.css('border-color', '');
332
+ field.parent().find('.error-message').remove();
333
+ }
334
+
266
335
  document.querySelectorAll('.toggle-option').forEach(el => {
267
336
  el.addEventListener('click', () => {
268
337
  document.querySelectorAll('.toggle-option').forEach(opt => opt.classList.remove('active-btn'));
@@ -43,7 +43,7 @@
43
43
  <path d="M2.5 8.3335H17.5" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
44
44
  </svg>
45
45
  </label>
46
- <input type="text" class="form-control center-input" id="startDate" placeholder="All Accounts" name="startDate">
46
+ <input type="text" class="form-control center-input" id="startDate" placeholder="All Logs" name="startDate">
47
47
  <div id="dash" class="dash">–</div>
48
48
  <input type="text" class="form-control right-input" id="endDate" placeholder="" name="endDate">
49
49
  <div id="toggle-wrapper" class="position-absolute" style="width: 100%; height: 100%;" onclick="document.getElementById('single-toggle').click()"></div>
@@ -1,3 +1,4 @@
1
+ <% count_text = count.nil? ? "20k+" : count.to_i %>
1
2
  <div class="col-md-4">
2
3
  <div class="dashboard-card border rounded-4 d-flex flex-column justify-content-between h-100">
3
4
  <div class="d-flex px-4 py-3 align-items-center gap-3">
@@ -6,7 +7,7 @@
6
7
  </div>
7
8
  <div>
8
9
  <h6 class="mb-0 fw-600 text-black-900"><%= title %></h6>
9
- <p class="text-muted fw-normal text-tertiary small mb-0"><%= count %></p>
10
+ <p class="text-muted fw-normal text-tertiary small mb-0"><%= count_text %></p>
10
11
  </div>
11
12
  </div>
12
13
  <div class="w-full dashboard-card-divider"></div>
@@ -91,7 +91,11 @@
91
91
 
92
92
  </td>
93
93
  <% end %>
94
- <td onClick="hightlightLinkedItems('<%= item.invoice_item_id %>', '<%= item.linked_invoice_item_id %>'); return false;"><%= item.pretty_plan_name.blank? || !item.item_type.in?(%w{USAGE RECURRING}) ? item.description : item.pretty_plan_name %></td>
94
+ <td onClick="hightlightLinkedItems('<%= item.invoice_item_id %>', '<%= item.linked_invoice_item_id %>'); return false;">
95
+ <span id="<%= item.invoice_item_id %>-popover" class="object-id-popover" data-id="<%= item.invoice_item_id %>" data-title="Invoice Item ID">
96
+ <%= item.pretty_plan_name.blank? || !item.item_type.in?(%w{USAGE RECURRING}) ? item.description : item.pretty_plan_name %>
97
+ </span>
98
+ </td>
95
99
  <td onClick="hightlightLinkedItems('<%= item.invoice_item_id %>', '<%= item.linked_invoice_item_id %>'); return false;"><%= item.start_date.html_safe if item.start_date %></td>
96
100
  <td onClick="hightlightLinkedItems('<%= item.invoice_item_id %>', '<%= item.linked_invoice_item_id %>'); return false;"><%= item.end_date.html_safe if item.end_date %></td>
97
101
  <td onClick="hightlightLinkedItems('<%= item.invoice_item_id %>', '<%= item.linked_invoice_item_id %>'); return false;"><%= item.subscription_id %></td>
@@ -149,4 +153,11 @@
149
153
  }
150
154
  });
151
155
  }
156
+
157
+ $(document).ready(function() {
158
+ // Initialize tooltips for invoice item IDs
159
+ if (typeof setObjectIdTooltip === 'function') {
160
+ setObjectIdTooltip();
161
+ }
162
+ });
152
163
  </script>
@@ -91,7 +91,7 @@
91
91
  <path d="M2.5 8.3335H17.5" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
92
92
  </svg>
93
93
  </label>
94
- <input type="text" class="form-control center-input" id="startDate" placeholder="All Accounts" name="startDate">
94
+ <input type="text" class="form-control center-input" id="startDate" placeholder="All Invoices" name="startDate">
95
95
  <div id="dash" class="dash">–</div>
96
96
  <input type="text" class="form-control right-input" id="endDate" placeholder="" name="endDate">
97
97
  <div id="toggle-wrapper" class="position-absolute" style="width: 100%; height: 100%;" onclick="document.getElementById('single-toggle').click()"></div>
@@ -90,7 +90,7 @@
90
90
  <path d="M2.5 8.3335H17.5" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
91
91
  </svg>
92
92
  </label>
93
- <input type="text" class="form-control center-input" id="startDate" placeholder="All Accounts" name="startDate">
93
+ <input type="text" class="form-control center-input" id="startDate" placeholder="All Payments" name="startDate">
94
94
  <div id="dash" class="dash">–</div>
95
95
  <input type="text" class="form-control right-input" id="endDate" placeholder="" name="endDate">
96
96
  <div id="toggle-wrapper" class="position-absolute" style="width: 100%; height: 100%;" onclick="document.getElementById('single-toggle').click()"></div>
data/lib/kaui/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kaui
4
- VERSION = '4.0.4'
4
+ VERSION = '4.0.6'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kaui
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.4
4
+ version: 4.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kill Bill core team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-12-09 00:00:00.000000000 Z
11
+ date: 2025-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -454,6 +454,7 @@ files:
454
454
  - app/controllers/kaui/tenants_controller.rb
455
455
  - app/controllers/kaui/transactions_controller.rb
456
456
  - app/helpers/kaui/account_helper.rb
457
+ - app/helpers/kaui/admin_allowed_users_helper.rb
457
458
  - app/helpers/kaui/application_helper.rb
458
459
  - app/helpers/kaui/date_helper.rb
459
460
  - app/helpers/kaui/exception_helper.rb