diff --git a/apimanager/base/context_processors.py b/apimanager/base/context_processors.py index d3dff76..7325b6f 100644 --- a/apimanager/base/context_processors.py +++ b/apimanager/base/context_processors.py @@ -13,7 +13,7 @@ USER_CURRENT = "/users/current" def api_version_processor(request): """Returns the configured API_VERSION""" - return {'API_VERSION': settings.API_VERSION['v500']} + return {'API_VERSION': settings.API_VERSION['v510']} def portal_page(request): @@ -82,7 +82,7 @@ def api_user_id(request): """Returns the API user id of the logged-in user""" user_id = 'not authenticated' get_current_user_api_url = USER_CURRENT - #Here we can not get the user from obp-api side, so we use the django auth user id here. + #Here we can not get the user from obp-api side, so we use the django auth user id here. cache_key_django_user_id = request.session._session.get('_auth_user_id') cache_key = '{},{},{}'.format('api_user_id',get_current_user_api_url, cache_key_django_user_id) apicaches=None @@ -112,4 +112,3 @@ def api_tester_url(request): """Returns the URL to the API Tester for the API instance""" url = getattr(settings, 'API_TESTER_URL', None) return {'API_TESTER_URL': url} - diff --git a/apimanager/consumers/forms.py b/apimanager/consumers/forms.py index 79f673b..0d5832c 100644 --- a/apimanager/consumers/forms.py +++ b/apimanager/consumers/forms.py @@ -12,8 +12,45 @@ class ApiConsumersForm(forms.Form): required=True, ) + from_date = forms.DateTimeField( + label='From Date', + widget=forms.DateTimeInput( + attrs={ + 'class': 'form-control', + 'type': 'datetime-local', + 'value': '2024-01-01T00:00', + } + ), + required=False, + initial='2024-01-01T00:00:00', + ) + + to_date = forms.DateTimeField( + label='To Date', + widget=forms.DateTimeInput( + attrs={ + 'class': 'form-control', + 'type': 'datetime-local', + 'value': '2026-01-01T00:00', + } + ), + required=False, + initial='2026-01-01T00:00:00', + ) + + per_second_call_limit = forms.IntegerField( + label='Per Second Call Limit', + widget=forms.NumberInput( + attrs={ + 'class': 'form-control', + } + ), + initial=-1, + required=False, + ) + per_minute_call_limit = forms.IntegerField( - label='per_minute_call_limit', + label='Per Minute Call Limit', widget=forms.NumberInput( attrs={ 'class': 'form-control', @@ -24,7 +61,7 @@ class ApiConsumersForm(forms.Form): ) per_hour_call_limit = forms.IntegerField( - label='per_hour_call_limit', + label='Per Hour Call Limit', widget=forms.NumberInput( attrs={ 'class': 'form-control', @@ -33,8 +70,9 @@ class ApiConsumersForm(forms.Form): initial=-1, required=False, ) + per_day_call_limit = forms.IntegerField( - label='per_day_call_limit', + label='Per Day Call Limit', widget=forms.NumberInput( attrs={ 'class': 'form-control', @@ -43,8 +81,9 @@ class ApiConsumersForm(forms.Form): initial=-1, required=False, ) + per_week_call_limit = forms.IntegerField( - label='per_week_call_limit', + label='Per Week Call Limit', widget=forms.NumberInput( attrs={ 'class': 'form-control', @@ -55,7 +94,7 @@ class ApiConsumersForm(forms.Form): ) per_month_call_limit = forms.IntegerField( - label='per_month_call_limit', + label='Per Month Call Limit', widget=forms.NumberInput( attrs={ 'class': 'form-control', diff --git a/apimanager/consumers/static/consumers/css/consumers.css b/apimanager/consumers/static/consumers/css/consumers.css index 7c502fc..5d47940 100644 --- a/apimanager/consumers/static/consumers/css/consumers.css +++ b/apimanager/consumers/static/consumers/css/consumers.css @@ -1,20 +1,184 @@ -.consumers #consumer-list { - margin-top: 20px; +.consumers #consumer-list { + margin-top: 20px; } #consumers .btn-group-vertical.filter-enabled, #consumers .btn-group-vertical.filter-apptype { - margin-top: 10px; + margin-top: 10px; } #consumers-detail div { - margin: 5px 0; + margin: 5px 0; } #consumers .filter a { - font-size: 12px; + font-size: 12px; } #consumers .actions .btn { - margin-bottom: 2px; + margin-bottom: 2px; +} + +/* Rate Limiting Styles */ +#consumers-detail h2 { + color: #333; + border-bottom: 2px solid #007bff; + padding-bottom: 10px; + margin-bottom: 20px; +} + +#consumers-detail .panel-info { + border-color: #bee5eb; + background-color: #d1ecf1; +} + +#consumers-detail .panel-info .panel-body { + background-color: #f8f9fa; + border-radius: 5px; + padding: 15px; +} + +#consumers-detail .text-info { + color: #0c5460 !important; + font-size: 16px; + font-weight: bold; +} + +#consumers-detail .text-muted { + color: #6c757d !important; + font-size: 12px; +} + +#consumers-detail .form-group label { + font-weight: bold; + color: #495057; +} + +#consumers-detail .btn-primary { + background-color: #007bff; + border-color: #007bff; + padding: 10px 20px; + font-weight: bold; +} + +#consumers-detail .btn-primary:hover { + background-color: #0056b3; + border-color: #0056b3; +} + +/* Usage statistics 6-column layout */ +#consumers-detail .panel-info .col-sm-2 { + min-height: 80px; + padding: 10px 5px; + transition: all 0.3s ease; +} + +/* Readonly fields styling */ +#consumers-detail input[readonly] { + background-color: #f8f9fa; + color: #6c757d; + cursor: not-allowed; + border: 1px solid #dee2e6; +} + +/* Refresh button styling */ +#refreshUsageBtn { + transition: all 0.3s ease; + margin-left: 10px; +} + +#refreshUsageBtn:hover { + background-color: #138496; + border-color: #117a8b; +} + +#refreshUsageBtn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* Progress bar styling */ +#refreshProgress { + height: 10px; + background-color: #f5f5f5; + border-radius: 5px; + overflow: hidden; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +#refreshProgress .progress-bar { + transition: width 0.3s ease; + background-color: #17a2b8; +} + +/* Usage update animation */ +.usage-calls { + transition: background-color 0.5s ease; +} + +.usage-calls.updating { + background-color: #d4edda !important; + padding: 2px 6px; + border-radius: 3px; +} + +/* Spinning animation for refresh icon */ +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.glyphicon-spin { + animation: spin 1s infinite linear; +} + +/* Panel pulse effect during refresh */ +.panel-refreshing { + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.7); + } + 70% { + box-shadow: 0 0 0 10px rgba(23, 162, 184, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(23, 162, 184, 0); + } +} + +/* Updated data highlight */ +.data-updated { + background-color: #d4edda; + border-left: 3px solid #28a745; + padding-left: 10px; + transition: all 0.5s ease; +} + +/* Responsive adjustments for usage stats */ +@media (max-width: 768px) { + #consumers-detail .panel-info .col-xs-6 { + margin-bottom: 15px; + } + + #consumers-detail .panel-info .col-sm-2 { + min-height: auto; + } + + #refreshUsageBtn { + font-size: 12px; + padding: 4px 8px; + } +} + +/* Timestamp fields in configuration section */ +#consumers-detail .form-group input[readonly] { + font-size: 12px; + padding: 6px 12px; } diff --git a/apimanager/consumers/static/consumers/js/consumers.js b/apimanager/consumers/static/consumers/js/consumers.js index 8da0463..cf25624 100644 --- a/apimanager/consumers/static/consumers/js/consumers.js +++ b/apimanager/consumers/static/consumers/js/consumers.js @@ -1,2 +1,100 @@ -$(document).ready(function($) { +$(document).ready(function ($) { + // Handle datetime-local inputs for rate limiting + function initializeDateTimeFields() { + // Set default values for datetime fields if they're empty + var fromDateField = $("#id_from_date"); + var toDateField = $("#id_to_date"); + + // If fields are empty, set default values + if (!fromDateField.val()) { + fromDateField.val("2024-01-01T00:00"); + } + if (!toDateField.val()) { + toDateField.val("2026-01-01T00:00"); + } + } + + // Convert ISO datetime strings to datetime-local format for form inputs + function convertISOToLocalDateTime(isoString) { + if (!isoString) return ""; + // Remove the 'Z' and convert to local datetime format + return isoString.replace("Z", "").substring(0, 16); + } + + // Initialize datetime fields with existing values if they exist + function setExistingDateTimeValues() { + var fromDate = $("[data-from-date]").data("from-date"); + var toDate = $("[data-to-date]").data("to-date"); + + if (fromDate && fromDate !== "1099-12-31T23:00:00Z") { + $("#id_from_date").val(convertISOToLocalDateTime(fromDate)); + } + if (toDate && toDate !== "1099-12-31T23:00:00Z") { + $("#id_to_date").val(convertISOToLocalDateTime(toDate)); + } + } + + // Form validation + function validateRateLimitingForm() { + $("form").on("submit", function (e) { + var hasError = false; + var errorMessage = ""; + + // Check if any limit values are negative (except -1 which means unlimited) + $('input[type="number"]').each(function () { + var value = parseInt($(this).val()); + if (value < -1) { + hasError = true; + errorMessage += + "Rate limit values must be -1 (unlimited) or positive numbers.\n"; + return false; + } + }); + + // Check date range + var fromDate = new Date($("#id_from_date").val()); + var toDate = new Date($("#id_to_date").val()); + + if (fromDate && toDate && fromDate > toDate) { + hasError = true; + errorMessage += "From Date must be before To Date.\n"; + } + + if (hasError) { + alert(errorMessage); + e.preventDefault(); + return false; + } + }); + } + + // Add visual feedback for current usage status + function enhanceUsageDisplay() { + $(".text-info").each(function () { + var callsMade = parseInt($(this).text().match(/\d+/)); + var parentDiv = $(this).closest(".col-xs-6, .col-sm-3"); + var limitText = parentDiv.find("strong").text().toLowerCase(); + + // You could add logic here to highlight usage that's approaching limits + // For now, we'll just ensure consistent styling + $(this).addClass("usage-indicator"); + }); + } + + // Initialize all functionality + initializeDateTimeFields(); + setExistingDateTimeValues(); + validateRateLimitingForm(); + enhanceUsageDisplay(); + + // Add tooltips for better UX + $('[data-toggle="tooltip"]').tooltip(); + + // Add help text for rate limiting fields + $('input[name*="call_limit"]').each(function () { + $(this).attr( + "title", + "Use -1 for unlimited, or enter a positive number for the limit", + ); + }); }); diff --git a/apimanager/consumers/templates/consumers/detail.html b/apimanager/consumers/templates/consumers/detail.html index 0569ed9..23ccfea 100644 --- a/apimanager/consumers/templates/consumers/detail.html +++ b/apimanager/consumers/templates/consumers/detail.html @@ -12,7 +12,7 @@