diff --git a/app/assets/stylesheets/kaui/auth_pages.css b/app/assets/stylesheets/kaui/auth_pages.css index 3fb6d9552..69a6c091f 100644 --- a/app/assets/stylesheets/kaui/auth_pages.css +++ b/app/assets/stylesheets/kaui/auth_pages.css @@ -2,6 +2,15 @@ min-height: 100vh; width: 100%; background-color: #fff; + overflow: hidden; +} + +.auth-container .centered-absolute { + position: relative; + top: 0; + left: 0; + transform: none; + margin: 1.5rem auto 0; } .auth-form-container { diff --git a/app/assets/stylesheets/kaui/kaui.css b/app/assets/stylesheets/kaui/kaui.css index 2d755645f..70e884ca7 100644 --- a/app/assets/stylesheets/kaui/kaui.css +++ b/app/assets/stylesheets/kaui/kaui.css @@ -16,209 +16,3 @@ image-url("glyphicons-halflings-regular.ttf") format("truetype"), image-url("glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg"); } - -.tag-bar { - border-bottom: 0.0625rem solid #ddd; - margin-bottom: 0; - position: relative; -} - -.tag-bar .tag-select { - display: inline-block; - vertical-align: middle; -} - -@media (max-width: 28.125rem) { - .tag-bar .tag-select { - width: 35%; - } -} - -.tag-bar .tag-select span { - padding: 0.3125rem 1.25rem; - text-align: center; - display: block; - border-right: 0.0625rem solid #ddd; -} - -@media (max-width: 48rem) { - .tag-bar .tag-select span { - border: 0; - } -} - -.tag-bar .tag-select span i { - margin: 0 0.3125rem; - color: #515151; - font-size: 1rem; -} - -.tag-bar .tag-select span i.fa-caret-down { - color: #999999; - font-size: 0.75rem; -} - -.tag-bar .tag-select span i.fa-tag { - font-size: 1.125rem; -} - -.tag-bar .tag-select span i.fa-money, -.tag-bar .tag-select span i.fa-credit-card { - font-size: 1rem; -} - -.tag-bar .tag-select:hover .tag-select-box { - max-height: 31.25rem; - padding: 1.25rem 1.5625rem; - border: 0.0625rem solid #ddd; - box-shadow: 0.0625rem 0.1875rem 0.125rem rgba(0, 0, 0, 0.11); -} - -.tag-bar .tag-select .tag-select-box { - position: absolute; - top: 100%; - left: -0.0625rem; - max-height: 0; - background-color: #fff; - z-index: 100; - padding: 0 0.9375rem; - font-size: 0.75rem; - color: #999999; - overflow: auto; - width: auto; - border-bottom: 0; - display: inline-block; -} - -@media (max-width: 48rem) { - .tag-bar .tag-select .tag-select-box { - border: 0; - } -} - -.tag-bar .tag-select .tag-select-box strong { - display: block; - font-weight: 400; - margin: 0 0 0.625rem; -} - -.tag-bar .tag-select .tag-select-box form { - display: inline-block; - width: 13.125rem; -} - -.tag-bar .tag-select .tag-select-box input[type="checkbox"] { - display: inline-block; - width: 9%; - margin: 0 1% 0 0; - line-height: 1.25rem; - vertical-align: middle; -} - -.tag-bar .tag-select .tag-select-box label { - font-size: 0.75rem; - display: inline-block; - width: 85%; - line-height: 1.25rem; - vertical-align: middle; - margin: 0; - font-weight: 400; -} - -.tag-bar .tag-select .tag-select-box textarea { - resize: none; - height: 5rem; - margin: 0 0 1.25rem; - width: 100%; -} - -.tag-bar .tag-select .tag-select-box a { - display: block; - font-size: 0.75rem; - line-height: 1.25rem; -} - -.tag-bar .tag-selected { - display: inline-block; - padding: 0.125rem 0.3125rem; - vertical-align: middle; -} - -@media (max-width: 48rem) { - .tag-bar .tag-selected { - width: 64%; - padding: 0.125rem; - overflow: hidden; - border-left: 0.0625rem solid #ddd; - } -} - -.tag-bar .tag-selected span { - display: inline-block; - font-size: 0.75rem; - color: #777777; - background: #e5e5e5; - padding: 0.1875rem 1.25rem; - margin: 0 0.3125rem; - transition: all 0.5s; - cursor: pointer; -} - -.tag-bar .tag-selected span:hover { - background-color: #cce9eb; -} - -@media (max-width: 48rem) { - .tag-bar .tag-selected span { - font-size: 0.625rem; - padding: 0.1875rem 0.3125rem; - display: block; - margin: 0 0 0.125rem; - } -} - -.tag-bar-breathe { - margin-bottom: 1.875rem; -} - -.tag-bar-no-border { - border-bottom: 0; -} - -.tag-bar-no-border .tag-select span { - border-right: 0; -} - -#main-menu .tag-bar .tag-select { - position: relative; -} - -#main-menu .tag-bar .tag-select-box { - word-break: keep-all; - white-space: nowrap; -} - -.inline-row-tag-bar { - padding-left: 0.625rem !important; -} - -.inline-row-tag-bar .tag-bar .tag-select span { - padding: 0.3125rem; - border-right: none; -} - -.inline-row-tag-bar .tag-bar .tag-select span i { - margin: 0 0.1875rem; - font-size: 0.75rem; -} - -.inline-row-tag-bar .tag-bar .tag-select span i.fa-tag { - font-size: 0.75rem; -} - -.inline-row-tag-bar .tag-bar .tag-select-box label { - white-space: nowrap; -} - - -/*# sourceMappingURL=kaui2.css.map */ diff --git a/app/assets/stylesheets/kaui/tags.css b/app/assets/stylesheets/kaui/tags.css index 638b064ef..af77aaea5 100644 --- a/app/assets/stylesheets/kaui/tags.css +++ b/app/assets/stylesheets/kaui/tags.css @@ -15,7 +15,7 @@ } } -.tag-bar .tag-select span { +.tag-bar .tag-select > span { padding: 0.3125rem 1.25rem; text-align: center; display: block; @@ -23,7 +23,7 @@ } @media (max-width: 48rem) { - .tag-bar .tag-select span { + .tag-bar .tag-select > span { border: 0; } } @@ -67,9 +67,8 @@ font-size: 0.75rem; color: #999999; overflow: hidden; - min-width: 15rem; - width: max-content; - max-width: min(25rem, 95vw); + width: 17rem; + max-width: min(17rem, 95vw); border-bottom: 0; display: inline-block; } @@ -107,6 +106,7 @@ vertical-align: middle; margin: 0; font-weight: 400; + overflow-wrap: anywhere; } .tag-bar .tag-select .tag-select-box textarea { @@ -169,7 +169,7 @@ border-bottom: 0; } -.tag-bar-no-border .tag-select span { +.tag-bar-no-border .tag-select > span { border-right: 0; } @@ -186,7 +186,7 @@ padding-left: 0.625rem !important; } -.inline-row-tag-bar .tag-bar .tag-select span { +.inline-row-tag-bar .tag-bar .tag-select > span { padding: 0.3125rem; border-right: none; } diff --git a/app/controllers/kaui/accounts_controller.rb b/app/controllers/kaui/accounts_controller.rb index 569249005..0ee2c0bc4 100644 --- a/app/controllers/kaui/accounts_controller.rb +++ b/app/controllers/kaui/accounts_controller.rb @@ -156,7 +156,15 @@ def show @children = wait(fetch_children) @account_parent = @account.parent_account_id.nil? ? nil : wait(fetch_parent) @email_notification_configuration = is_email_notifications_plugin_available ? wait(fetch_email_notification_configuration) : [] - @bundles = wait(fetch_bundles) + @bundles = begin + wait(fetch_bundles) + rescue KillBillClient::API::ResponseError => e + # e.g. a subscription references a plan that no longer exists in the currently loaded catalog. + # Don't fail the whole account page for this, just warn and show it without bundle-derived data. + Rails.logger.warn("Unable to fetch bundles for account #{@account.account_id}: #{as_string(e)}") + flash.now[:warning] = "Unable to load some subscription details: #{as_string(e)}" + [] + end @last_transaction_by_payment_method_id = {} wait(fetch_payments).each do |payment| diff --git a/app/helpers/kaui/date_helper.rb b/app/helpers/kaui/date_helper.rb index 4fc155f1f..154724d09 100644 --- a/app/helpers/kaui/date_helper.rb +++ b/app/helpers/kaui/date_helper.rb @@ -24,6 +24,18 @@ def truncate_millis(date_s) DateTime.parse(date_s).strftime('%FT%T') end + # Best-effort epoch (seconds) timestamp for a timeline entry's date/time + # value, used purely for client-side "Sort by Date" ordering on the + # account timeline page. Returns nil if the date is missing or cannot be + # parsed (entries without a usable timestamp are sorted last). + def timeline_sort_timestamp(date) + return nil if date.blank? + + DateTime.parse(date.to_s).to_i + rescue ArgumentError, TypeError + nil + end + # Retrieve current killbill server time based on a time zone. # if no time zone is provided it will return UTC. # if the killbill server is not reachable it will return the time of the Kaui server diff --git a/app/services/dependencies/aviate.rb b/app/services/dependencies/aviate.rb index 4b0622ca2..e41860981 100644 --- a/app/services/dependencies/aviate.rb +++ b/app/services/dependencies/aviate.rb @@ -21,10 +21,13 @@ class << self # Any error (plugin not installed, missing/invalid JWT, network issue, etc.) is treated as # "not Aviate" so that callers can safely fall back to the existing XML-catalog behavior. def aviate_catalog?(account_id, options_for_klient) - response = KillBillClient::API.get("#{KILLBILL_AVIATE_PREFIX}/catalog/info", catalog_params(account_id), request_options(options_for_klient)) - JSON.parse(response.body)['state'] == CATALOG_STATE_AVIATE - rescue StandardError => e - Rails.logger.warn("Failed to retrieve Aviate catalog info: #{e.class}: #{e.message}") + params_list = account_id.present? ? [{ accountId: account_id }, {}] : [{}] + params_list.each do |params| + response = KillBillClient::API.get("#{KILLBILL_AVIATE_PREFIX}/catalog/info", params, request_options(options_for_klient)) + return true if JSON.parse(response.body)['state'] == CATALOG_STATE_AVIATE + rescue StandardError => e + Rails.logger.warn("Failed to retrieve Aviate catalog info (params=#{params}): #{e.class}: #{e.message}") + end false end diff --git a/app/views/kaui/account_timelines/show.html.erb b/app/views/kaui/account_timelines/show.html.erb index e121dcb5c..73032eefc 100644 --- a/app/views/kaui/account_timelines/show.html.erb +++ b/app/views/kaui/account_timelines/show.html.erb @@ -49,7 +49,14 @@ -