diff --git a/Gemfile b/Gemfile index 0bec5644..77f90f87 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,10 @@ gemspec gem 'rails', '~> 7.0.1' +# Lock minitest to 5.x until Rails 7.1+ adds Minitest 6.0 support +# Minitest 6.0.0 was released Dec 2024 with breaking API changes +gem 'minitest', '~> 5.0' + # This fix is temporary until the next release of the gem # See https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror gem 'concurrent-ruby', '1.3.4' diff --git a/app/assets/javascripts/kaui/kaui_override.js b/app/assets/javascripts/kaui/kaui_override.js index 75e14622..1b3c5377 100644 --- a/app/assets/javascripts/kaui/kaui_override.js +++ b/app/assets/javascripts/kaui/kaui_override.js @@ -439,7 +439,7 @@ function setObjectIdPopover(){ // Custom tooltip function for object IDs // attributes: // data-id = content of the tooltip, object id; required -// title = title of the tooltip; not required +// data-title = title of the tooltip; not required function setObjectIdTooltip() { // Remove any existing tooltips $(".custom-tooltip").remove(); @@ -476,6 +476,9 @@ function showCustomTooltip(element, objectId) { var position = $element.offset(); var elementHeight = $element.outerHeight(); var elementWidth = $element.outerWidth(); + + // Get custom title from data-title attribute or default to "Subscription ID" + var tooltipTitle = $element.data("title") || "Subscription ID"; // Create tooltip content with header and custom SVG icon var svgIcon = @@ -485,7 +488,7 @@ function showCustomTooltip(element, objectId) { ""; var tooltipContent = - '
Subscription ID
' + + '
' + tooltipTitle + '
' + '
' + '' + objectId + diff --git a/app/controllers/kaui/admin_allowed_users_controller.rb b/app/controllers/kaui/admin_allowed_users_controller.rb index 3410e0c9..54ea5c90 100644 --- a/app/controllers/kaui/admin_allowed_users_controller.rb +++ b/app/controllers/kaui/admin_allowed_users_controller.rb @@ -6,6 +6,10 @@ class AdminAllowedUsersController < Kaui::EngineController def index @allowed_users = retrieve_allowed_users_for_current_user + @roles_by_user = {} + @allowed_users.each do |user| + @roles_by_user[user.kb_username] = roles_for_user(user) + end end def new diff --git a/app/controllers/kaui/payments_controller.rb b/app/controllers/kaui/payments_controller.rb index e75849fc..862c720a 100644 --- a/app/controllers/kaui/payments_controller.rb +++ b/app/controllers/kaui/payments_controller.rb @@ -24,6 +24,7 @@ def download if all_fields_checked columns = KillBillClient::Model::PaymentAttributes.instance_variable_get('@json_attributes') - %w[transactions audit_logs] + columns += %w[payment_date status] # additional fields not part of the model csv_headers = columns.dup Kaui::Payment::REMAPPING_FIELDS.each do |k, v| index = csv_headers.index(k) diff --git a/app/helpers/kaui/admin_allowed_users_helper.rb b/app/helpers/kaui/admin_allowed_users_helper.rb new file mode 100644 index 00000000..28e8d277 --- /dev/null +++ b/app/helpers/kaui/admin_allowed_users_helper.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Kaui + module AdminAllowedUsersHelper + # Check if a user can be deleted + # Returns false if the user is an admin or the current logged-in user + def can_delete_user?(user, user_roles) + is_admin = user_roles.include?('admin') || user.kb_username == Kaui.root_username + is_current_user = user.kb_username == current_user.kb_username + + !is_admin && !is_current_user + end + end +end diff --git a/app/views/kaui/admin_allowed_users/index.html.erb b/app/views/kaui/admin_allowed_users/index.html.erb index 85de7f78..2df058fd 100644 --- a/app/views/kaui/admin_allowed_users/index.html.erb +++ b/app/views/kaui/admin_allowed_users/index.html.erb @@ -60,11 +60,15 @@ <%= link_to u.kb_username, admin_allowed_user_path(u.id) %> <%= u.description %> - <%= form_tag kaui_engine.admin_allowed_user_path(u.id), method: :delete, class: 'd-inline', onsubmit: "return confirm('Are you sure?');" do %> - <%= button_tag type: 'submit', class: 'btn btn-outline-secondary d-inline-flex align-items-center gap-1 kaui-button delete-button custom-hover' do %> - Delete + <% user_roles = @roles_by_user[u.kb_username] || [] %> + <% if can_delete_user?(u, user_roles) %> + <%= form_tag kaui_engine.admin_allowed_user_path(u.id), method: :delete, class: 'd-inline', onsubmit: "return confirm('Are you sure?');" do %> + <%= button_tag type: 'submit', class: 'btn btn-outline-secondary d-inline-flex align-items-center gap-1 kaui-button delete-button custom-hover' do %> + Delete + <% end %> <% end %> <% end %> + <%= link_to kaui_engine.edit_admin_allowed_user_path(u.id), class: 'text-decoration-none' do %> <%= render "kaui/components/button/button", { label: "Edit", diff --git a/app/views/kaui/admin_allowed_users/show.html.erb b/app/views/kaui/admin_allowed_users/show.html.erb index 1dfcbc58..f7795dd7 100644 --- a/app/views/kaui/admin_allowed_users/show.html.erb +++ b/app/views/kaui/admin_allowed_users/show.html.erb @@ -6,21 +6,23 @@

User details

- <%= render "kaui/components/button/button", { - label: 'Delete', - variant: "outline-secondary d-inline-flex align-items-center gap-1", - type: "button", - html_class: "kaui-button delete-button custom-hover", - html_options: { - data: { - confirm: "Are you sure?", - method: "delete", - turbo: false, # Optional: disable Turbo if using Rails Turbo - "url": kaui_engine.admin_allowed_user_path(@allowed_user.id) - }, - onclick: "if (confirm(this.dataset.confirm)) { Rails.ajax({ url: this.dataset.url, type: this.dataset.method }); }" - } - } %> + <% if can_delete_user?(@allowed_user, @roles) %> + <%= render "kaui/components/button/button", { + label: 'Delete', + variant: "outline-secondary d-inline-flex align-items-center gap-1", + type: "button", + html_class: "kaui-button delete-button custom-hover", + html_options: { + data: { + confirm: "Are you sure?", + method: "delete", + turbo: false, # Optional: disable Turbo if using Rails Turbo + "url": kaui_engine.admin_allowed_user_path(@allowed_user.id) + }, + onclick: "if (confirm(this.dataset.confirm)) { Rails.ajax({ url: this.dataset.url, type: this.dataset.method }); }" + } + } %> + <% end %> <%= link_to kaui_engine.edit_admin_allowed_user_path(@allowed_user.id), :class => '' do %> diff --git a/app/views/kaui/admin_tenants/new_catalog.html.erb b/app/views/kaui/admin_tenants/new_catalog.html.erb index 3e4487ff..5ca31677 100644 --- a/app/views/kaui/admin_tenants/new_catalog.html.erb +++ b/app/views/kaui/admin_tenants/new_catalog.html.erb @@ -264,55 +264,58 @@ $(document).ready(function() { recompute_available_base_products_for_ao(); }); - // Custom validation since HTML5 validation is not working - // (possibly due to jQuery autocomplete or other JavaScript interference) - $('#catalog_simple form').on('submit', function(e) { - var isValid = true; - var pattern = /^[a-zA-Z_][\w\.-]*$/; - - // Validate Product Name - var productName = $('#simple_plan_product_name').val().trim(); - var productNameField = $('#simple_plan_product_name'); - - if (productName === '') { - showError(productNameField, 'Product Name is required'); - isValid = false; - } else if (!pattern.test(productName)) { - showError(productNameField, 'Product Name must start with a letter or underscore, and can only contain letters, digits, underscores, hyphens, and periods'); - isValid = false; - } else { - clearError(productNameField); - } - - // Validate Plan Name - var planName = $('#simple_plan_plan_id').val().trim(); - var planNameField = $('#simple_plan_plan_id'); - - if (planName === '') { - showError(planNameField, 'Plan Name is required'); - isValid = false; - } else if (!pattern.test(planName)) { - showError(planNameField, 'Plan Name must start with a letter or underscore, and can only contain letters, digits, underscores, hyphens, and periods'); - isValid = false; - } else { - clearError(planNameField); - } - - if (!isValid) { - e.preventDefault(); - e.stopPropagation(); - // Scroll to first error - $('.error-message:first').get(0)?.scrollIntoView({ behavior: 'smooth', block: 'center' }); - return false; - } - - return true; - }); + // Realtime validation function + function validateField(field) { + var pattern = /^[a-zA-Z_][\w\.-]*$/; + var value = field.val().trim(); + var fieldName = field.attr('id') === 'simple_plan_product_name' ? 'Product Name' : 'Plan Name'; + + if (value === '') { + showError(field, fieldName + ' is required'); + return false; + } else if (!pattern.test(value)) { + showError(field, fieldName + ' must start with a letter or underscore, and can only contain letters, digits, underscores, hyphens, and periods'); + return false; + } else { + clearError(field); + return true; + } + } + + // Realtime validation on input + $('#simple_plan_product_name, #simple_plan_plan_id').on('input', function() { + validateField($(this)); + }); + + // Validation on blur (when user leaves the field) + $('#simple_plan_product_name, #simple_plan_plan_id').on('blur', function() { + validateField($(this)); + }); + + // Custom validation on form submit + $('#catalog_simple form').on('submit', function(e) { + var isValid = true; + + // Validate Product Name + if (!validateField($('#simple_plan_product_name'))) { + isValid = false; + } - // Clear errors on input - $('#simple_plan_product_name, #simple_plan_plan_id').on('input', function() { - clearError($(this)); - }); + // Validate Plan Name + if (!validateField($('#simple_plan_plan_id'))) { + isValid = false; + } + + if (!isValid) { + e.preventDefault(); + e.stopPropagation(); + // Scroll to first error + $('.error-message:first').get(0)?.scrollIntoView({ behavior: 'smooth', block: 'center' }); + return false; + } + + return true; + }); }); function showError(field, message) { diff --git a/app/views/kaui/invoices/_invoice_table.html.erb b/app/views/kaui/invoices/_invoice_table.html.erb index 3e2f6658..a2117c93 100644 --- a/app/views/kaui/invoices/_invoice_table.html.erb +++ b/app/views/kaui/invoices/_invoice_table.html.erb @@ -91,7 +91,11 @@ <% end %> - <%= item.pretty_plan_name.blank? || !item.item_type.in?(%w{USAGE RECURRING}) ? item.description : item.pretty_plan_name %> + + + <%= item.pretty_plan_name.blank? || !item.item_type.in?(%w{USAGE RECURRING}) ? item.description : item.pretty_plan_name %> + + <%= item.start_date.html_safe if item.start_date %> <%= item.end_date.html_safe if item.end_date %> <%= item.subscription_id %> @@ -149,4 +153,11 @@ } }); } + + $(document).ready(function() { + // Initialize tooltips for invoice item IDs + if (typeof setObjectIdTooltip === 'function') { + setObjectIdTooltip(); + } + });