)
+ */
+$(document).on('click', '.host-line', function (e) {
+ // Ignore clicks on links or the checkbox itself
+ if ($(e.target).closest('a, input[name="checkbox-host[]"]').length) {
+ return;
+ }
+
+ // Find the checkbox inside this container and toggle it
+ var checkbox = $(this).find('input[name="checkbox-host[]"]');
+
+ if (checkbox.length) {
+ checkbox.click();
+ }
+});
+
+/**
+ * Event: toggle selected state on host-line when checkbox is checked/unchecked
+ */
+$(document).on('change', 'input[name="checkbox-host[]"]', function () {
+ var container = $(this).closest('.host-line');
+
+ if ($(this).is(':checked')) {
+ container.addClass('host-selected');
+ } else {
+ container.removeClass('host-selected');
+ }
+
+ const group = $(this).attr('group');
+ const button = $('.select-group-hosts-btn[group="' + group + '"]');
+ const groupCheckboxes = $('input[name="checkbox-host[]"][group="' + group + '"]').filter(function () {
+ return $(this).closest('.host-line').is(':visible');
+ });
+ const checkedCount = groupCheckboxes.filter(':checked').length;
+ const isAllSelected = groupCheckboxes.length > 0 && checkedCount === groupCheckboxes.length;
+
+ button.toggleClass('hide', checkedCount === 0);
+ button.attr('status', isAllSelected ? 'selected' : '');
+ button.find('.select-group-hosts-checkbox').prop('checked', isAllSelected);
+});
+
/**
* Event: when a host checkbox is checked
*/
@@ -113,38 +156,43 @@ $(document).on('click','input[type="checkbox"][name="checkbox-host[]"]',function
/**
* Event: when a 'Select all' button is clicked, it selects all checkbox-host[] of the group
*/
-$(document).on('click','input[type="checkbox"].select-group-hosts-checkbox',function () {
+$(document).on('click', '.select-group-hosts-btn', function () {
// Retrieve the group name of the button which has been clicked
const group = $(this).attr('group');
+ const checkbox = $(this).find('.select-group-hosts-checkbox');
// Retrieve checkbox status
const status = $(this).attr('status');
- // Retrieve all checkboxes of the group
- const hostsCheckboxes = $('input[name="checkbox-host[]"][group="' + group + '"]:visible');
+ // Retrieve all checkboxes of the group (filter by visible host-line parent since checkboxes are hidden)
+ const hostsCheckboxes = $('input[name="checkbox-host[]"][group="' + group + '"]').filter(function () {
+ return $(this).closest('.host-line').is(':visible');
+ });
// If current status is 'selected', then unselect all hosts
if (status == 'selected') {
hostsCheckboxes.each(function () {
if ($(this).is(':checked')) {
- $(this).prop('checked', false);
+ $(this).prop('checked', false).trigger('change');
}
});
- // Set status to 'unselected'
- $(this).attr('status', 'unselected');
+ // Clear selected status
+ $(this).attr('status', '');
+ checkbox.prop('checked', false);
// Make sure the 'Select all hosts' button is unchecked
} else {
// Check all checkbox-host[] of the same group
hostsCheckboxes.each(function () {
if (!$(this).is(':checked')) {
- $(this).prop('checked', true);
+ $(this).prop('checked', true).trigger('change');
}
});
// Set status to 'selected'
$(this).attr('status', 'selected');
+ checkbox.prop('checked', true);
}
// Print actions for selected hosts
@@ -158,12 +206,14 @@ $(document).on('click', '#select-all-hosts', function () {
/**
* Retrieve all host groups checkboxes that are visible
*/
- const hostGroupsCheckboxes = $('input[type="checkbox"].select-group-hosts-checkbox:visible');
+ const hostGroupsButtons = $('.select-group-hosts-btn');
/**
- * Retrieve all hosts checkboxes that are visible
+ * Retrieve all hosts checkboxes (filter by visible host-line parent)
*/
- const hostCheckboxes = $('input[type="checkbox"][name="checkbox-host[]"]:visible');
+ const hostCheckboxes = $('input[type="checkbox"][name="checkbox-host[]"]').filter(function () {
+ return $(this).closest('.host-line').is(':visible');
+ });
/**
* Retrieve select status
@@ -174,15 +224,18 @@ $(document).on('click', '#select-all-hosts', function () {
* If current status is not 'selected', then select all hosts
*/
if (selectStatus != 'selected') {
- hostGroupsCheckboxes.each(function () {
- if (!$(this).is(':checked')) {
- $(this).prop('checked', true);
+ hostGroupsButtons.each(function () {
+ const checkbox = $(this).find('.select-group-hosts-checkbox');
+
+ if (!checkbox.is(':checked')) {
+ checkbox.prop('checked', true);
+ $(this).attr('status', 'selected');
}
});
hostCheckboxes.each(function () {
if (!$(this).is(':checked')) {
- $(this).prop('checked', true);
+ $(this).prop('checked', true).trigger('change');
}
});
@@ -198,20 +251,23 @@ $(document).on('click', '#select-all-hosts', function () {
* Otherwise, unselect all hosts
*/
} else {
- hostGroupsCheckboxes.each(function () {
- if ($(this).is(':checked')) {
- $(this).prop('checked', false);
+ hostGroupsButtons.each(function () {
+ const checkbox = $(this).find('.select-group-hosts-checkbox');
+
+ if (checkbox.is(':checked')) {
+ checkbox.prop('checked', false);
+ $(this).attr('status', '');
}
});
hostCheckboxes.each(function () {
if ($(this).is(':checked')) {
- $(this).prop('checked', false);
+ $(this).prop('checked', false).trigger('change');
}
});
- // Set status to 'unselected'
- $(this).attr('status', 'unselected');
+ // Clear selected status
+ $(this).attr('status', '');
// Make sure the 'Select all hosts' button is unchecked
$(this).css('opacity', '');
@@ -294,16 +350,39 @@ $(document).on('submit', '#host-update-packages-form', function () {
// Print success alert:
true,
// Print error alert:
- true,
- // Reload container:
- [],
- // Execute functions on success:
- []
+ true
);
return false;
});
+/**
+ * Event: click on a host-package-item to select it
+ */
+$(document).on('click', '.host-package-item', function (e) {
+ // Ignore if clicked on a link, the checkbox itself, or the package timeline trigger
+ if ($(e.target).closest('a, .get-package-timeline').length || $(e.target).is('.available-package-checkbox')) {
+ return;
+ }
+
+ var checkbox = $(this).find('.available-package-checkbox');
+ if (checkbox.length) {
+ checkbox.trigger('click');
+ }
+});
+
+/**
+ * Event: toggle selected state on host-package-item when checkbox changes
+ */
+$(document).on('change', '.available-package-checkbox', function () {
+ var container = $(this).closest('.host-package-item');
+ if (this.checked) {
+ container.addClass('host-package-selected');
+ } else {
+ container.removeClass('host-package-selected');
+ }
+});
+
/**
* Event: available package checkbox selection
*/
@@ -370,7 +449,7 @@ $(document).on('click','input[type="checkbox"].available-package-checkbox',funct
myconfirmbox.print(
{
'title': 'Update packages',
- 'message': 'Request the host to update selected packages?',
+ 'message': countChecked + ' package' + (countChecked > 1 ? 's' : '') + ' selected.',
'id': 'update-available-packages-confirm-box',
'buttons': [
{
@@ -406,6 +485,9 @@ $(document).on('click','input[type="checkbox"].available-package-checkbox',funct
} else {
myconfirmbox.close();
}
+
+ // Trigger change to update visual state
+ $(this).trigger('change');
});
/**
@@ -486,7 +568,6 @@ $(document).on('click','#host-request-btn',function () {
buttons.push(
{
'text': 'Request general information',
- 'color': 'blue-alt',
'callback': function () {
executeAction('request-general-infos', [id]);
}
@@ -498,7 +579,6 @@ $(document).on('click','#host-request-btn',function () {
buttons.push(
{
'text': 'Request package information',
- 'color': 'blue-alt',
'callback': function () {
executeAction('request-packages-infos', [id]);
}
@@ -510,7 +590,6 @@ $(document).on('click','#host-request-btn',function () {
buttons.push(
{
'text': 'Update packages',
- 'color': 'blue-alt',
'callback': function () {
mypanel.get('hosts/requests/update-packages', {
hostsId: [id]
diff --git a/www/public/resources/js/events/hosts/layout.js b/www/public/resources/js/events/hosts/layout.js
index 202ad300b..8ab218128 100644
--- a/www/public/resources/js/events/hosts/layout.js
+++ b/www/public/resources/js/events/hosts/layout.js
@@ -64,7 +64,7 @@ $(document).on('mouseenter', '.search-host-tooltip', function (e) {
content += '';
content += '
• Main search is performed on all fields, use filters to narrow down results
';
content += '
• You can combine multiple filters
';
- content += '
• Use quotes when filter contains spaces, e.g os="Linux Mint"
';
+ content += '
• Use quotes when a string contains spaces, e.g os="Linux Mint"
';
content += '
• Search and filters are case-insensitive
';
content += '
';
diff --git a/www/public/resources/js/events/hosts/settings.js b/www/public/resources/js/events/hosts/settings.js
index ec81c4932..4486afaba 100644
--- a/www/public/resources/js/events/hosts/settings.js
+++ b/www/public/resources/js/events/hosts/settings.js
@@ -13,6 +13,7 @@ $(document).on('submit','form#hosts-settings',function (e) {
{
complianceThresholdCount: $('input[name="compliance-threshold-count"').val(),
complianceThresholdDays: $('input[name="compliance-threshold-days"').val(),
+ complianceSecurityUpdate: $('input[name="compliance-security-update"').is(':checked') ? 1 : 0,
complianceRebootRequired: $('input[name="compliance-reboot-required"').is(':checked') ? 1 : 0
},
// Print success alert:
diff --git a/www/public/resources/js/events/repo/edit.js b/www/public/resources/js/events/repo/edit.js
index b9cbbbf82..60ec8ddcf 100644
--- a/www/public/resources/js/events/repo/edit.js
+++ b/www/public/resources/js/events/repo/edit.js
@@ -1,75 +1,24 @@
/**
- * Event: click on edit repository button
- * TODO: wait for repos list refactoring before implementing the repo-edit-btn
+ * Event: click on rename repository button
*/
-// $(document).on('click','.repo-edit-btn',function (e) {
-// e.preventDefault();
-
-// const repoId = $(this).attr('repo-id');
-
-// /**
-// * The buttons that will be displayed in the confirm box
-// */
-// var buttons = [];
-
-// /**
-// * The list of allowed actions the user can execute on the selected repositories
-// * By default: all, unless the user has specific permissions
-// * Those permissions are later verified by the server so even if the user tries to execute an action he is not allowed to, it will not work
-// */
-// var allowedActions = ['update', 'duplicate', 'env', 'rebuild', 'rename', 'edit', 'install', 'delete'];
-
-// /**
-// * Get permissions from cookie
-// */
-// if (mycookie.exists('user_permissions')) {
-// var userPermissions = JSON.parse(mycookie.get('user_permissions'));
-
-// // Reset allowed actions array
-// var allowedActions = [];
-
-// // Loop through all permissions and check if the user has the permission to execute the action
-// if (userPermissions.repositories && userPermissions.repositories['allowed-actions']) {
-// var allowedActions = userPermissions.repositories['allowed-actions'];
-// }
-// }
-
-// /**
-// * Define confirm box buttons depending on the allowed actions
-// */
-// if (allowedActions.includes('rename')) {
-// buttons.push(
-// {
-// 'text': 'Rename',
-// 'color': 'blue-alt',
-// 'callback': function () {
-// executeAction('rename');
-// }
-// }
-// );
-// }
-
-// if (allowedActions.includes('edit')) {
-// buttons.push(
-// {
-// 'text': 'Edit',
-// 'color': 'blue-alt',
-// 'callback': function () {
-// executeAction('edit')
-// }
-// }
-// );
-// }
-
-// myconfirmbox.print(
-// {
-// 'title': 'Edit repository',
-// 'message': '',
-// 'id': 'repo-edit-confirm-box',
-// 'buttons': buttons
-// }
-// );
-// });
+$(document).on('click','.repo-rename-btn',function (e) {
+ e.preventDefault(e);
+
+ // The buttons that will be displayed in the confirm box
+ var buttons = [];
+
+ if (!myrepopermission.allowedAction('rename')) {
+ myalert.print('You do not have permission to rename repositories', 'error');
+ return;
+ }
+
+ // Get panel
+ mypanel.get('repos/rename', {
+ repos: JSON.stringify([{
+ 'repo-id': $(this).attr('repo-id')
+ }])
+ });
+});
/**
* Event: submit repository edit form
@@ -155,7 +104,7 @@ $(document).on('submit','#edit-form',function () {
// Controller:
'repo/edit',
// Action:
- 'validateForm',
+ 'validate-execute',
// Data:
{
params: paramsJson,
@@ -166,49 +115,171 @@ $(document).on('submit','#edit-form',function () {
true
).then(function () {
// Uncheck all checkboxes and remove all styles JQuery could have applied
- $('.reposList').find('input[name=checkbox-repo]').prop('checked', false);
- $('.reposList').find('input[name=checkbox-repo]').removeAttr('style');
+ $('#repositories-list').find('input[name=checkbox-repo]').prop('checked', false);
+ $('#repositories-list').find('input[name=checkbox-repo]').removeAttr('style');
});
return false;
});
/**
- * Event: add placeholder to description input on mouse enter
- * This is to prevent Firefox from always displaying the placeholder
+ * Event: click on "Add a description" button (header)
+ */
+$(document).on('click', '.repo-add-description-btn', function () {
+ const btn = $(this);
+ const repoItem = btn.closest('.repo-item');
+ const container = repoItem.find('.repo-description-container');
+ const p = container.find('p.repo-description-input');
+
+ // Hide the button, reveal the description container and enter edit mode
+ btn.hide();
+ container.show();
+ myrepo.startEditDescription(p);
+});
+
+/**
+ * Event: single-click on an empty description to add one
*/
-$(document).on('mouseenter','input[type="text"].repo-description-input',function () {
- $(this).attr('placeholder', '🖉 add a description');
+$(document).on('click', 'p.repo-description-input.repo-description-empty', function () {
+ myrepo.startEditDescription($(this));
});
/**
- * Event: remove placeholder on mouse leave
- * This is to prevent Firefox from always displaying the placeholder
+ * Event: double-click on an existing description to edit it
*/
-$(document).on('mouseleave','input[type="text"].repo-description-input',function () {
- $(this).attr('placeholder', '');
+$(document).on('dblclick', 'p.repo-description-input', function () {
+ myrepo.startEditDescription($(this));
});
/**
* Event: edit repository description when pressing 'Enter' key
*/
-$(document).on('keypress','input[type="text"].repo-description-input',function (e) {
+$(document).on('keypress','.repo-description-input-edit',function (e) {
e.stopPropagation();
const keycode = (e.keyCode ? e.keyCode : e.which);
if (keycode == '13') {
- myenvironment.updateDescription($(this).attr('env-id'), $(this).val());
+ const input = $(this);
+ const p = input.closest('p.repo-description-input');
+ const repoItem = p.closest('.repo-item');
+ const newDescription = input.val().trim();
+
+ // Mark as saved to prevent blur from reverting.
+ // Use a native property (not jQuery .data) because p.text() below calls
+ // .empty()/cleanData on the input, which would wipe jQuery data before the
+ // synchronous blur (fired when the focused input is removed) can read it.
+ this._descSaved = true;
+
+ // Save description
+ myrepo.updateDescription(input.attr('repo-id'), newDescription);
+
+ // Revert to with new value
+ p.text(newDescription);
+
+ // If description is now empty, hide the container and show the header button again
+ if (!newDescription) {
+ p.addClass('repo-description-empty');
+ repoItem.find('.repo-description-container').hide();
+ repoItem.find('.repo-add-description-btn').show();
+ } else {
+ p.removeClass('repo-description-empty');
+ }
}
});
/**
- * Event: create new repo: print description field only if an env is specified
+ * Event: revert description input on blur (click outside)
*/
-$(document).on('change','#new-repo-target-env-select',function () {
- if ($('#new-repo-target-env-select').val() == "") {
- $('#new-repo-target-description-tr').hide();
+$(document).on('blur','.repo-description-input-edit',function () {
+ const input = $(this);
+
+ // If already saved via Enter, do nothing (native flag survives cleanData)
+ if (this._descSaved) {
+ return;
+ }
+
+ const p = input.closest('p.repo-description-input');
+ const repoItem = p.closest('.repo-item');
+ const originalDescription = input.attr('data-original');
+
+ // Revert to
with original value
+ p.text(originalDescription);
+
+ // If description is empty, hide the container and show the header button again
+ if (!originalDescription) {
+ p.addClass('repo-description-empty');
+ repoItem.find('.repo-description-container').hide();
+ repoItem.find('.repo-add-description-btn').show();
} else {
- $('#new-repo-target-description-tr').show();
+ p.removeClass('repo-description-empty');
}
-}).trigger('change');
+});
+
+/**
+ * Event: click on "Add tags" button (header)
+ */
+$(document).on('click', '.repo-add-tags-btn', function () {
+ const btn = $(this);
+ const repoItem = btn.closest('.repo-item');
+ const container = repoItem.find('.repo-tags-container');
+ const display = container.find('div.repo-tags-display');
+
+ // Hide the button, reveal the tags container and enter edit mode
+ btn.hide();
+ container.show();
+ myrepo.startEditTags(display);
+});
+
+/**
+ * Event: single-click on an empty tags area to add tags
+ */
+$(document).on('click', '.repo-tags-display.repo-tags-empty', function () {
+ myrepo.startEditTags($(this));
+});
+
+/**
+ * Event: single-click on a tag to filter the repositories search by it
+ */
+$(document).on('click', '.repo-tag-item', function (e) {
+ e.stopPropagation();
+
+ const tag = $(this).find('p').text().trim();
+
+ if (tag) {
+ myrepo.searchByTag(tag);
+ }
+});
+
+/**
+ * Event: double-click on existing tags to edit them
+ */
+$(document).on('dblclick', '.repo-tags-display', function () {
+ myrepo.startEditTags($(this));
+});
+
+/**
+ * Event: save tags when clicking outside the tags editor
+ */
+$(document).on('mousedown', function (e) {
+ const editing = $('.repo-tags-display.editing');
+
+ if (editing.length === 0) {
+ return;
+ }
+
+ editing.each(function () {
+ const display = $(this);
+
+ // Ignore clicks inside the editor itself or inside the select2 dropdown/container
+ if ($(e.target).closest('.repo-tags-display').is(display)) {
+ return;
+ }
+
+ if ($(e.target).closest('.select2-container, .select2-dropdown').length > 0) {
+ return;
+ }
+
+ myrepo.saveRepoTags(display);
+ });
+});
diff --git a/www/public/resources/js/events/repo/env.js b/www/public/resources/js/events/repo/env.js
index 29b4f1274..2b63b55d4 100644
--- a/www/public/resources/js/events/repo/env.js
+++ b/www/public/resources/js/events/repo/env.js
@@ -1,21 +1,45 @@
/**
- * Event: select one or multiple snapshot environment(s) to delete
+ * Event: click on a snap-env-container to toggle the environment checkbox
*/
-$(document).on('click','.select-env-checkbox',function (e) {
- // Prevent parent to be triggered
+$(document).on('click', '.snap-env-container', function (e) {
+ // Prevent triggering the parent snap-container click
e.stopPropagation();
- var actions = [];
+ // Toggle the hidden checkbox
+ var checkbox = $(this).find('.select-env-checkbox');
+
+ if (checkbox.is(':disabled')) {
+ return;
+ }
+
+ checkbox.prop('checked', !checkbox.prop('checked')).trigger('change');
+});
+
+/**
+ * Event: toggle selected state and confirmbox when env checkbox changes
+ */
+$(document).on('change', '.select-env-checkbox', function (e) {
+ // Prevent parent to be triggered
+ e.stopPropagation(e);
+
+ if ($(this).is(':disabled')) {
+ $(this).prop('checked', false);
+ return;
+ }
- // If the checkbox is checked, make it visible, else remove any custom visibility so it returns to default
+ var container = $(this).closest('.snap-env-container');
+
+ // Toggle visual state
if ($(this).is(':checked')) {
- $(this).css('visibility', 'visible');
+ container.addClass('env-selected');
} else {
- $(this).css('visibility', '');
+ container.removeClass('env-selected');
}
+ var actions = [];
+
// Get all checked checkboxes
- const checked = $('.reposList').find('input[type="checkbox"].select-env-checkbox:checked');
+ const checked = $('#repositories-list').find('input[type="checkbox"].select-env-checkbox:checked');
if (checked.length == 0) {
myconfirmbox.close();
@@ -51,7 +75,7 @@ $(document).on('click','.select-env-checkbox',function (e) {
// Controller:
'task',
// Action:
- 'validateForm',
+ 'validate-execute',
// Data:
{
taskParams: JSON.stringify([actions[i]]),
@@ -62,6 +86,10 @@ $(document).on('click','.select-env-checkbox',function (e) {
true
);
}
+
+ // A repo item still holding a checked checkbox is considered busy and would be
+ // skipped by the repos list partial reload, hiding the environment removal
+ checked.prop('checked', false).closest('.snap-env-container').removeClass('env-selected');
}
}
]
diff --git a/www/public/resources/js/group.js b/www/public/resources/js/events/repo/group.js
similarity index 87%
rename from www/public/resources/js/group.js
rename to www/public/resources/js/events/repo/group.js
index e70d1d4ab..a8e9fd89c 100644
--- a/www/public/resources/js/group.js
+++ b/www/public/resources/js/events/repo/group.js
@@ -1,11 +1,7 @@
-/**
- * Events listeners
- */
-
/**
* Event: Create new group
*/
-$(document).on('submit','#newGroupForm',function (e) {
+$(document).on('submit','form#new-group',function (e) {
e.preventDefault();
ajaxRequest(
@@ -15,7 +11,7 @@ $(document).on('submit','#newGroupForm',function (e) {
'new',
// Data:
{
- name: $("#newGroupInput").val(),
+ name: $(this).find('input[name="group-name"]').val(),
type: 'repo'
},
// Print success alert:
@@ -75,7 +71,5 @@ $(document).on('submit','.group-form',function (e) {
* Event: Print group configuration div
*/
$(document).on('click','.group-config-btn',function () {
- var id = $(this).attr('group-id');
-
- slide('.group-config-div[group-id="' + id + '"]');
+ slide('.group-config-div[group-id="' + $(this).attr('group-id') + '"]');
});
diff --git a/www/public/resources/js/events/repo/list.js b/www/public/resources/js/events/repo/list.js
index 9f5bd318d..f554b6a6c 100644
--- a/www/public/resources/js/events/repo/list.js
+++ b/www/public/resources/js/events/repo/list.js
@@ -11,18 +11,18 @@ $(document).on('click','#hide-all-repo-groups',function () {
$(this).find('img').attr('src', 'assets/icons/view-off.svg');
// Retrieve all groups and hide them if they are visible
- $('.repo-list-group-container').each(function () {
- // Retrieve group id
- var id = $(this).attr('group-id');
+ $('.repos-list-group').each(function () {
+ var content = $(this).children('.group-content');
- // If the group is visible then hide it, else do nothing
- if ($(this).is(":visible")) {
- slide('.repo-list-group-container[group-id="' + id + '"]');
+ // If the group content is visible then hide it, else do nothing
+ if (content.is(":visible")) {
+ content.slideUp(200);
}
});
// Change all up/down icons to 'down'
$('img.hide-repo-group').attr('src', 'assets/icons/view-off.svg');
+ $('img.hide-repo-group').attr('state', 'hidden');
}
// If actual state is 'hidden' then show all groups
@@ -32,18 +32,18 @@ $(document).on('click','#hide-all-repo-groups',function () {
$(this).find('img').attr('src', 'assets/icons/view.svg');
// Retrieve all groups and show them if they are hidden
- $('.repo-list-group-container').each(function () {
- // Retrieve group id
- var id = $(this).attr('group-id');
+ $('.repos-list-group').each(function () {
+ var content = $(this).children('.group-content');
- // If the group is hidden then show it, else do nothing
- if ($(this).is(":hidden")) {
- slide('.repo-list-group-container[group-id="' + id + '"]');
+ // If the group content is hidden then show it, else do nothing
+ if (content.is(":hidden")) {
+ content.slideDown(200);
}
});
// Change all up/down icons to 'up'
$('img.hide-repo-group').attr('src', 'assets/icons/view.svg');
+ $('img.hide-repo-group').attr('state', 'visible');
}
});
@@ -51,7 +51,6 @@ $(document).on('click','#hide-all-repo-groups',function () {
* Event: show / hide repos group content
*/
$(document).on('click','.hide-repo-group',function () {
- var id = $(this).attr('group-id');
var state = $(this).attr('state');
if (state == 'visible') {
@@ -64,5 +63,5 @@ $(document).on('click','.hide-repo-group',function () {
$(this).attr('src', 'assets/icons/view.svg');
}
- slide('.repo-list-group-container[group-id="' + id + '"]');
+ $(this).closest('.repos-list-group').children('.group-content').slideToggle(200);
});
diff --git a/www/public/resources/js/events/repo/preferences.js b/www/public/resources/js/events/repo/preferences.js
new file mode 100644
index 000000000..c95229f9f
--- /dev/null
+++ b/www/public/resources/js/events/repo/preferences.js
@@ -0,0 +1,39 @@
+/**
+ * Event: edit user personnal informations
+ */
+$(document).on('submit','form#repos-list-preferences',function (e) {
+ e.preventDefault();
+
+ var preferences = {};
+
+ // Get all values from the form
+ $(this).find('input').each(function () {
+ var name = $(this).attr('name');
+
+ // If the input is a checkbox, get its checked state
+ if ($(this).attr('type') === 'checkbox') {
+ preferences[name] = $(this).is(':checked');
+ } else {
+ preferences[name] = $(this).val();
+ }
+ });
+
+ ajaxRequest(
+ // Controller:
+ 'user/preferences',
+ // Action:
+ 'save',
+ // Data:
+ {
+ preferences: preferences
+ },
+ // Print success alert:
+ true,
+ // Print error alert:
+ true
+ ).then(function () {
+ mycontainer.reload('repos/list');
+ });
+
+ return false;
+});
\ No newline at end of file
diff --git a/www/public/resources/js/events/repo/scheduled-task.js b/www/public/resources/js/events/repo/scheduled-task.js
new file mode 100644
index 000000000..b6decc306
--- /dev/null
+++ b/www/public/resources/js/events/repo/scheduled-task.js
@@ -0,0 +1,20 @@
+/**
+ * Event: show the parameters of the selected action
+ */
+$(document).on('change', '#scheduled-task-action', function () {
+ myscheduledtask.printActionParams();
+});
+
+/**
+ * Event: refresh the number of targeted repositories when a target filter changes
+ */
+$(document).on('change', '.scheduled-task-target-param', function () {
+ myscheduledtask.refreshCount();
+});
+
+/**
+ * Event: create the scheduled task
+ */
+$(document).on('click', '.scheduled-task-confirm-btn', function () {
+ myscheduledtask.schedule();
+});
diff --git a/www/public/resources/js/events/settings/environment.js b/www/public/resources/js/events/settings/environment.js
index 44c97d2de..2d3732886 100644
--- a/www/public/resources/js/events/settings/environment.js
+++ b/www/public/resources/js/events/settings/environment.js
@@ -1,11 +1,36 @@
+/**
+ * Toggle environment proctection
+ */
+$(document).on('click','img.env-protection', function () {
+ const img = $(this);
+ const protected = img.attr('protected');
+ const id = img.attr('env-id');
+ const name = img.attr('env-name');
+
+ if (protected == 'false') {
+ img.attr('src','/assets/icons/locked-red.svg');
+ img.attr('title','Unprotect environment');
+ img.attr('protected','true');
+ img.addClass('icon');
+ img.removeClass('icon-lowopacity');
+ } else {
+ img.attr('src','/assets/icons/unlocked.svg');
+ img.attr('title','Protect environment');
+ img.addClass('icon-lowopacity');
+ img.attr('protected','false');
+ img.removeClass('icon');
+ }
+});
+
/**
* Event: add a new environment by pressing enter
*/
$(document).on('keypress','input[name="add-env-name"]', function (e) {
- var keycode = (event.keyCode ? event.keyCode : event.which);
+ const keycode = (event.keyCode ? event.keyCode : event.which);
+
if (keycode == '13') {
e.stopPropagation();
- addEnv();
+ myenvironment.add();
}
});
@@ -13,17 +38,18 @@ $(document).on('keypress','input[name="add-env-name"]', function (e) {
* Event: add a new environment by clicking the add button
*/
$(document).on('click','#add-env-btn',function () {
- addEnv();
+ myenvironment.add();
});
/**
* Event: edit environments by pressing enter
*/
$(document).on('keypress','input[name="env-name"]', function (e) {
- var keycode = (event.keyCode ? event.keyCode : event.which);
+ const keycode = (event.keyCode ? event.keyCode : event.which);
+
if (keycode == '13') {
e.stopPropagation();
- editEnv();
+ myenvironment.edit();
}
});
@@ -31,15 +57,15 @@ $(document).on('keypress','input[name="env-name"]', function (e) {
* Event: edit environments by clicking the edit button
*/
$(document).on('click','#edit-env-btn', function () {
- editEnv()
+ myenvironment.edit();
});
/**
* Event: delete an environment
*/
$(document).on('click','.delete-env-btn',function () {
- var id = $(this).attr('env-id');
- var name = $(this).attr('env-name');
+ const id = $(this).attr('env-id');
+ const name = $(this).attr('env-name');
myconfirmbox.print(
{
@@ -50,22 +76,7 @@ $(document).on('click','.delete-env-btn',function () {
'text': 'Delete',
'color': 'red',
'callback': function () {
- ajaxRequest(
- // Controller:
- 'environment',
- // Action:
- 'delete-env',
- // Data:
- {
- id: id,
- },
- // Print success alert:
- true,
- // Print error alert:
- true
- ).then(function () {
- mylayout.reloadContentById('envs-div');
- });
+ myenvironment.delete(id);
}
}]
}
diff --git a/www/public/resources/js/events/snapshot/browse.js b/www/public/resources/js/events/snapshot/browse.js
new file mode 100644
index 000000000..73969da93
--- /dev/null
+++ b/www/public/resources/js/events/snapshot/browse.js
@@ -0,0 +1,120 @@
+/**
+ * Event: view snapshot file content
+ */
+$(document).on('click','.view-file',function () {
+ const name = $(this).attr('name');
+
+ mymodal.loading();
+
+ ajaxRequest(
+ // Controller:
+ 'repo/snapshot/browse',
+ // Action:
+ 'view-file',
+ // Data:
+ {
+ path: $(this).attr('path')
+ },
+ // Print success alert:
+ false,
+ // Print error alert:
+ true
+ ).then(function () {
+ // Print the modal window with the log
+ mymodal.print(jsonValue.message, name, true);
+ });
+});
+
+/**
+ * Event: show confirm box when selecting packages
+ */
+$(document).on('click',".package-checkbox",function (e) {
+ // Prevent parent to be triggered
+ e.stopPropagation();
+
+ const snapId = $('#packages-list').attr('snap-id');
+
+ // The buttons that will be displayed in the confirm box
+ var buttons = [];
+
+ // If no checkbox is selected then hide the buttons
+ if ($('body').find('input[name=packageName\\[\\]]:checked').length == 0) {
+ myconfirmbox.close();
+ return;
+ }
+
+ // Count the number of selected packages (across both tree and search results)
+ var count = $('body').find('input[type="checkbox"].package-checkbox:checked').length;
+
+ // Define confirm box buttons depending on the allowed actions
+
+ // Download is always allowed by default
+ buttons.push(
+ {
+ 'text': 'Download',
+ 'callback': function () {
+ mysnapshot.download();
+ }
+ }
+ );
+
+ if (myrepopermission.allowedAction('delete-package')) {
+ buttons.push(
+ {
+ 'text': 'Delete',
+ 'color': 'red',
+ 'callback': function () {
+ mysnapshot.delete(snapId);
+ }
+ }
+ );
+ }
+
+ // Show the confirm box
+ myconfirmbox.print(
+ {
+ 'title': 'Select packages',
+ 'message': count + ' file(s) selected.',
+ 'id': 'select-package',
+ 'buttons': buttons
+ }
+ );
+});
+
+/**
+ * Event: submit the snapshot package upload form
+ * Captures the form submission and uploads the selected packages via ajax
+ */
+$(document).on('submit', '#snapshot-upload', function (e) {
+ e.preventDefault();
+
+ // Upload the selected packages
+ mysnapshot.upload(this);
+});
+
+/**
+ * Event: rebuild metadata
+ */
+$(document).on('click',"#snapshot-rebuild-btn",function () {
+ ajaxRequest(
+ // Controller:
+ 'repo/snapshot/browse',
+ // Action:
+ 'rebuild',
+ // Data:
+ {
+ snapId: $(this).attr('snap-id'),
+ gpgSign: $('input[type=checkbox][name=gpgSign]').is(':checked') ? 'true' : 'false'
+ },
+ // Print success alert:
+ true,
+ // Print error alert:
+ true
+ ).then(function () {
+ // Reload containers
+ mycontainer.reload('snapshot/kpi');
+ mycontainer.reload('snapshot/list');
+ // Close panel
+ mypanel.close('snapshot/rebuild');
+ });
+});
diff --git a/www/public/resources/js/events/task/actions.js b/www/public/resources/js/events/task/actions.js
index b381dc9ba..90d90fcf5 100644
--- a/www/public/resources/js/events/task/actions.js
+++ b/www/public/resources/js/events/task/actions.js
@@ -20,33 +20,13 @@ $(document).on('click','.show-step-content-btn',function () {
if ($('.task-step-content[task-id="' + taskId + '"][step="' + step + '"]').is(':visible')) {
$('.task-step-content[task-id="' + taskId + '"][step="' + step + '"]').hide();
+ $(this).removeClass('step-open');
} else {
$('.task-step-content[task-id="' + taskId + '"][step="' + step + '"]').css('display', 'grid');
+ $(this).addClass('step-open');
}
});
-/**
- * Event: show logfile
- */
-$(document).on('click','.show-task-btn',function () {
- var taskId = $(this).attr('task-id');
-
- // Add a loading spinner to the container
- $('#log-refresh-container').html('

');
-
- // Change URL without reloading the page
- history.pushState(null, null, '/run/' + taskId);
-
- // Reload task container to print the new task log
- mycontainer.reload('tasks/log').then(function () {
- // Restart log scroll event listener
- scrollEventListener();
-
- // Remove loading spinner
- $('#log-refresh-container .loading-veil').remove();
- });
-});
-
/**
* Event: view task process log (debug log)
*/
@@ -234,6 +214,27 @@ $(document).on('click','.step-down-btn',function () {
$('.task-step-content[task-id="' + taskId + '"][step="' + step + '"]').scrollTop($('.task-step-content[task-id="' + taskId + '"][step="' + step + '"]').scrollTop() + 100);
});
+/**
+ * Event: click on a selectable task item to toggle its hidden checkbox
+ */
+$(document).on('click', '.task-item-selectable', function (e) {
+ // Prevent parent to be triggered
+ e.stopPropagation(e);
+
+ // Ignore clicks on action icons and the details button
+ if ($(e.target).closest('.show-scheduled-task-info-btn, .relaunch-task-btn, .stop-task-btn, .task-checkbox-input, .view-task-log-btn').length) {
+ return;
+ }
+
+ // Find the hidden checkbox inside this task item and toggle it
+ var checkbox = $(this).find('.task-checkbox-input');
+
+ if (checkbox.length) {
+ // click() toggles the native checked state + fires click and change events
+ checkbox.click();
+ }
+});
+
/**
* Event: show or hide scheduled task informations
*/
@@ -249,6 +250,19 @@ $(document).on('click','.show-scheduled-task-info-btn',function (e) {
$('.scheduled-task-info[task-id="' + taskId + '"]').toggle();
});
+/**
+ * Event: toggle selected state on task-item when its hidden checkbox is toggled
+ */
+$(document).on('change', '.task-checkbox-input', function () {
+ var container = $(this).closest('.task-item');
+
+ if ($(this).is(':checked')) {
+ container.addClass('task-selected');
+ } else {
+ container.removeClass('task-selected');
+ }
+});
+
/**
* Event: relaunch task
*/
@@ -256,6 +270,8 @@ $(document).on('click','.relaunch-task-btn',function (e) {
// Prevent parent to be triggered
e.stopPropagation();
+ myalert.print('Relaunching task...');
+
ajaxRequest(
// Controller:
'task',
@@ -269,5 +285,7 @@ $(document).on('click','.relaunch-task-btn',function (e) {
true,
// Print error alert:
true
- );
+ ).then(function () {
+ mycontainer.reload('tasks/tasks');
+ });
});
\ No newline at end of file
diff --git a/www/public/resources/js/events/task/stop.js b/www/public/resources/js/events/task/stop.js
index 73b63c333..73d8e2695 100644
--- a/www/public/resources/js/events/task/stop.js
+++ b/www/public/resources/js/events/task/stop.js
@@ -1,7 +1,10 @@
/**
* Event: stop task
*/
-$(document).on('click','.stop-task-btn',function () {
+$(document).on('click','.stop-task-btn',function (e) {
+ // Prevent parent to be triggered
+ e.stopPropagation();
+
myalert.print('Stopping task...');
ajaxRequest(
@@ -17,5 +20,7 @@ $(document).on('click','.stop-task-btn',function () {
true,
// Print error alert:
true
- );
+ ).then(function () {
+ mycontainer.reload('tasks/tasks');
+ });
});
\ No newline at end of file
diff --git a/www/public/resources/js/functions.js b/www/public/resources/js/functions.js
index 3a88a68b5..0f0eb6b1d 100644
--- a/www/public/resources/js/functions.js
+++ b/www/public/resources/js/functions.js
@@ -209,19 +209,24 @@ function empty(value)
*/
function printEnv(env, selector)
{
- // Default colors
- var background = '#ffffff';
- var color = '#000000';
+ // Default: outlined style with subtle gray
+ var color = '#c0d0e2';
+ var border = '1.5px solid #c0d0e2';
+ var background = 'transparent';
// Check if the environment color is set in localStorage
if (localStorage.getItem('env/' + env) !== null) {
- definition = JSON.parse(localStorage.getItem('env/' + env));
- color = definition.color;
- background = definition.background;
+ var definition = JSON.parse(localStorage.getItem('env/' + env));
+
+ if (definition.background && definition.background !== '#ffffff') {
+ // Use configured color for border and text (outlined style)
+ color = definition.background;
+ border = '1.5px solid ' + definition.background;
+ }
}
// Generate html
- var html = '⸺' + env + '';
+ var html = '⸺' + env + '';
// Print environment
$(selector).html(html);
diff --git a/www/public/resources/js/functions/browse.js b/www/public/resources/js/functions/browse.js
deleted file mode 100644
index 97418185f..000000000
--- a/www/public/resources/js/functions/browse.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * Download package
- */
-function downloadPackage()
-{
- packagesToDownload = [];
-
- /**
- * Get all selected checkboxes and their file-id (media) attribute
- */
- $('#packages-list, #browse-search-results').find('input[name=packageName\\[\\]]:checked').each(function () {
- packagesToDownload.push({ filename: $(this).attr('filename'), path: $(this).attr('path') });
- });
-
- /**
- * Append a temporary ) or an environment tag (.snap-env)
+ */
+$(document).on('click', '.snap-container', function (e) {
+ // Ignore clicks on links, environment tags/containers, or the checkbox itself
+ if ($(e.target).closest('a, .snap-env-container, .snap-env, input[name="checkbox-repo"]').length) {
+ return;
+ }
+
+ // Find the checkbox inside this container and toggle it
+ var checkbox = $(this).find('input[name="checkbox-repo"]');
+
+ if (checkbox.length) {
+ checkbox.click();
+ }
+});
+
+/**
+ * Event: toggle selected state on snap-container when checkbox is checked/unchecked
+ */
+$(document).on('change', 'input[name="checkbox-repo"]', function () {
+ var container = $(this).closest('.snap-container');
+
+ if ($(this).is(':checked')) {
+ container.addClass('snap-selected');
+ } else {
+ container.removeClass('snap-selected');
+ }
+});
+
/**
* Event: when a checkbox is checked/unchecked
*/
$(document).on('click',"input[name=checkbox-repo]",function () {
- /**
- * The buttons that will be displayed in the confirm box
- */
+ // The buttons that will be displayed in the confirm box
var buttons = [];
- /**
- * The list of allowed actions the user can execute on the selected repositories
- * By default: all, unless the user has specific permissions
- * Those permissions are later verified by the server so even if the user tries to execute an action he is not allowed to, it will not work
- */
- var allowedActions = ['update', 'duplicate', 'env', 'rebuild', 'rename', 'edit', 'install', 'delete'];
-
- /**
- * Retrieve checkbox's group id
- */
- var groupId = $(this).attr('group-id');
+ // Retrieve checkbox's group id
+ const groupId = $(this).attr('group-id');
- /**
- * Count the number of checked checkboxes
- */
- var count_checked = $('.reposList').find('input[name=checkbox-repo]:checked').length;
+ // Count the number of checked checkboxes
+ const count_checked = $('#repositories-list').find('input[name=checkbox-repo]:checked').length;
- /**
- * If all checkboxes are unchecked then we hide all action buttons
- */
+ // If all checkboxes are unchecked then we hide all action buttons
if (count_checked == 0) {
myconfirmbox.close();
- $('.reposList').find('input[name=checkbox-repo]').removeAttr('style');
- $('.repos-list-group[group-id=' + groupId + ']').find('.repos-list-group-select-all-btns').hide();
+ $('#repositories-list').find('input[name=checkbox-repo]').removeAttr('style');
+ $('#repositories-list').find('.snap-container').removeClass('snap-selected');
+ $('.repos-list-group[group-id=' + groupId + ']').find('.repos-list-group-select-latest-btns').addClass('hide').hide();
+ $('.repos-list-group[group-id=' + groupId + ']').find('.repos-list-select-all-btns').addClass('hide').hide();
+ $('.repos-list-group-select-latest-btns[group-id="' + groupId + '"]').attr('status', '').find('input[type="checkbox"]').prop('checked', false);
+ $('.repos-list-select-all-btns[group-id="' + groupId + '"]').attr('status', '').find('input[type="checkbox"]').prop('checked', false);
return;
}
- /**
- * Get permissions from cookie
- */
- if (mycookie.exists('user_permissions')) {
- var userPermissions = JSON.parse(mycookie.get('user_permissions'));
-
- // Reset allowed actions array
- var allowedActions = [];
-
- // Loop through all permissions and check if the user has the permission to execute the action
- if (userPermissions.repositories && userPermissions.repositories['allowed-actions']) {
- var allowedActions = userPermissions.repositories['allowed-actions'];
- }
- }
-
- /**
- * Define confirm box buttons depending on the allowed actions
- */
- if (allowedActions.includes('update')) {
+ // Define confirm box buttons depending on the allowed actions
+ if (myrepopermission.allowedAction('update')) {
buttons.push(
{
'text': 'Update',
- 'color': 'blue-alt',
'callback': function () {
executeAction('update')
}
@@ -195,11 +187,10 @@ $(document).on('click',"input[name=checkbox-repo]",function () {
);
}
- if (allowedActions.includes('duplicate')) {
+ if (myrepopermission.allowedAction('duplicate')) {
buttons.push(
{
'text': 'Duplicate',
- 'color': 'blue-alt',
'callback': function () {
executeAction('duplicate');
}
@@ -207,11 +198,10 @@ $(document).on('click',"input[name=checkbox-repo]",function () {
);
}
- if (allowedActions.includes('env')) {
+ if (myrepopermission.allowedAction('env')) {
buttons.push(
{
'text': 'Point an environment',
- 'color': 'blue-alt',
'callback': function () {
executeAction('env');
}
@@ -219,11 +209,10 @@ $(document).on('click',"input[name=checkbox-repo]",function () {
);
}
- if (allowedActions.includes('rebuild')) {
+ if (myrepopermission.allowedAction('rebuild')) {
buttons.push(
{
'text': 'Rebuild',
- 'color': 'blue-alt',
'callback': function () {
executeAction('rebuild');
}
@@ -231,23 +220,10 @@ $(document).on('click',"input[name=checkbox-repo]",function () {
);
}
- if (allowedActions.includes('rename')) {
- buttons.push(
- {
- 'text': 'Rename',
- 'color': 'blue-alt',
- 'callback': function () {
- executeAction('rename');
- }
- }
- );
- }
-
- if (allowedActions.includes('edit')) {
+ if (myrepopermission.allowedAction('edit')) {
buttons.push(
{
'text': 'Edit',
- 'color': 'blue-alt',
'callback': function () {
executeAction('edit')
}
@@ -259,14 +235,13 @@ $(document).on('click',"input[name=checkbox-repo]",function () {
buttons.push(
{
'text': 'Install',
- 'color': 'blue-alt',
'callback': function () {
executeAction('install')
}
}
);
- if (allowedActions.includes('delete')) {
+ if (myrepopermission.allowedAction('delete')) {
buttons.push(
{
'text': 'Delete',
@@ -287,19 +262,31 @@ $(document).on('click',"input[name=checkbox-repo]",function () {
}
);
- /**
- * Show 'select all latest snapshots' buttons
- */
- $('.repos-list-group[group-id=' + groupId + ']').find('.repos-list-group-select-all-btns').css('display', 'flex');
+ // Show 'select all latest snapshots' buttons
+ $('.repos-list-group[group-id=' + groupId + ']').find('.repos-list-group-select-latest-btns').removeClass('hide').css('display', 'flex');
+ $('.repos-list-group[group-id=' + groupId + ']').find('.repos-list-select-all-btns').removeClass('hide').css('display', 'flex');
/**
* If there is at least 1 checkbox checked then we display all the other checkboxes
* All checked checkboxes are set to opacity = 1
*/
- $('.reposList').find('input[name=checkbox-repo]').css("visibility", "visible");
- $('.reposList').find('input[name=checkbox-repo]:checked').css("opacity", "1");
+ $('#repositories-list').find('input[name=checkbox-repo]').css("visibility", "visible");
+ $('#repositories-list').find('input[name=checkbox-repo]:checked').css("opacity", "1");
+
+ syncRepoGroupSelectionButtons(groupId);
});
+function syncRepoGroupSelectionButtons(groupId)
+{
+ const checkedInGroup = $('.repos-list-group[group-id=' + groupId + ']').find('input[name=checkbox-repo]:checked').length;
+
+ // Keep both buttons in sync when a group has no selected snapshots.
+ if (checkedInGroup === 0) {
+ $('.repos-list-group-select-latest-btns[group-id="' + groupId + '"]').attr('status', '').find('input[type="checkbox"]').prop('checked', false);
+ $('.repos-list-select-all-btns[group-id="' + groupId + '"]').attr('status', '').find('input[type="checkbox"]').prop('checked', false);
+ }
+}
+
function executeAction(action)
{
var repos = [];
@@ -307,7 +294,7 @@ function executeAction(action)
/**
* Loop through all checked repos and retrieve their id
*/
- $('.reposList').find('input[name=checkbox-repo]:checked').each(function () {
+ $('#repositories-list').find('input[name=checkbox-repo]:checked').each(function () {
var obj = {};
/**
@@ -352,43 +339,41 @@ function executeAction(action)
}
/**
- * Event: Click on 'select all latest snapshots' button
+ * Event: Click on snapshots selection buttons ('latest' or 'all')
*/
-$(document).on('click',".repos-list-group-select-all-btns",function () {
- /**
- * Retrieve group Id
- */
- var groupId = $(this).attr('group-id');
+$(document).on('click', '.repos-list-group-select-latest-btns, .repos-list-select-all-btns', function () {
+ var latestRepoId = '';
+ const isLatestButton = $(this).hasClass('repos-list-group-select-latest-btns');
+ const buttonSelector = isLatestButton ? '.repos-list-group-select-latest-btns' : '.repos-list-select-all-btns';
+ const oppositeButtonSelector = isLatestButton ? '.repos-list-select-all-btns' : '.repos-list-group-select-latest-btns';
- /**
- * Retrieve all repos in the group
- */
- var reposCheckboxes = $('.repos-list-group[group-id=' + groupId + ']').find('input[name=checkbox-repo]');
+ // Retrieve group Id
+ const groupId = $(this).attr('group-id');
- /**
- * Retrieve select status
- */
- var selectStatus = $(this).attr('status');
+ // Retrieve all repos in the group
+ const reposCheckboxes = $('.repos-list-group[group-id=' + groupId + ']').find('input[name=checkbox-repo]');
- /**
- * If current status is not 'selected', then select all the latest snaps
- */
- if (selectStatus != 'selected') {
- /**
- * Loop through all checkboxes and check the latest snap
- * The latest snap is the first snap in the list (the first checkbox of each repo)
- */
- latestRepoId = '';
+ // Retrieve select status
+ const status = $(this).attr('status');
+
+ // If current status is not 'selected', then select snapshots according to button type
+ if (status != 'selected') {
reposCheckboxes.each(function () {
- // Click the checkbox if it matches the latest snap
- if ($(this).attr('repo-id') != latestRepoId) {
- // Click the checkbox if not already checked
- if (!$(this).is(':checked')) {
- $(this).click();
+ if (isLatestButton) {
+ // The latest snap is the first snap in the list for each repo.
+ if ($(this).attr('repo-id') != latestRepoId) {
+ if (!$(this).is(':checked')) {
+ $(this).click();
+ }
+ } else {
+ // Make sure only latest snapshots remain selected.
+ if ($(this).is(':checked')) {
+ $(this).click();
+ }
}
- // Otherwise, uncheck the checkbox to make sure only the latest snaps are checked
} else {
- if ($(this).is(':checked')) {
+ // Select all snapshots.
+ if (!$(this).is(':checked')) {
$(this).click();
}
}
@@ -397,35 +382,37 @@ $(document).on('click',".repos-list-group-select-all-btns",function () {
});
// Set status
- $(this).attr('status', 'selected');
+ $(buttonSelector + '[group-id="' + groupId + '"]').attr('status', 'selected');
+ $(oppositeButtonSelector + '[group-id="' + groupId + '"]').attr('status', '').find('input[type="checkbox"]').prop('checked', false);
- // Make sure the 'Select latest snapshots' button is visible and its checkbox is checked
- $('.repos-list-group-select-all-btns[group-id="' + groupId + '"]').css('display', 'flex');
- $('.repos-list-group-select-all-btns[group-id="' + groupId + '"]').css('opacity', '1');
- $('.repos-list-group-select-all-btns[group-id="' + groupId + '"]').css('filter', 'initial');
- $('.repos-list-group-select-all-btns[group-id="' + groupId + '"]').find('input[type="checkbox"]').prop('checked', true);
+ // Make sure the active selection button is visible and checked.
+ $(buttonSelector + '[group-id="' + groupId + '"]').removeClass('hide').css('display', 'flex');
+ $(buttonSelector + '[group-id="' + groupId + '"]').css('opacity', '1');
+ $(buttonSelector + '[group-id="' + groupId + '"]').css('filter', 'initial');
+ $(buttonSelector + '[group-id="' + groupId + '"]').find('input[type="checkbox"]').prop('checked', true);
- /**
- * Otherwise, uncheck all checkboxes
- */
- } else {
- reposCheckboxes.each(function () {
- if ($(this).is(':checked')) {
- $(this).click();
- }
- });
+ return;
+ }
- // Set status
- $(this).attr('status', '');
+ // Otherwise, uncheck all checkboxes in the group
+ reposCheckboxes.each(function () {
+ if ($(this).is(':checked')) {
+ $(this).click();
+ }
+ });
+
+ // Set status and visual state on both buttons when no selection remains.
+ syncRepoGroupSelectionButtons(groupId);
- // Make sure the 'Select latest snapshots' button is hidden and its checkbox is unchecked
- $('.repos-list-group-select-all-btns[group-id="' + groupId + '"]').hide();
- $('.repos-list-group-select-all-btns[group-id="' + groupId + '"]').css('opacity', '');
- $('.repos-list-group-select-all-btns[group-id="' + groupId + '"]').css('filter', '');
- $('.repos-list-group-select-all-btns[group-id="' + groupId + '"]').find('input[type="checkbox"]').prop('checked', false);
+ if ($('.repos-list-group[group-id=' + groupId + ']').find('input[name=checkbox-repo]:checked').length === 0) {
+ $('.repos-list-group-select-latest-btns[group-id="' + groupId + '"]').addClass('hide').hide().css('opacity', '').css('filter', '');
+ $('.repos-list-select-all-btns[group-id="' + groupId + '"]').addClass('hide').hide().css('opacity', '').css('filter', '');
}
});
+
+
+
/**
* Event: Schedule a task
*/
@@ -440,11 +427,13 @@ $(document).on('click',".task-schedule-btn", function () {
*/
if ($(this).is(':checked')) {
form.find('.task-schedule-params').show();
- form.find('.task-confirm-btn').css('background-color', '#15bf7f');
+ form.find('.task-confirm-btn').removeClass('btn-large-red');
+ form.find('.task-confirm-btn').addClass('btn-large-green');
form.find('.task-confirm-btn').html('Schedule');
} else {
form.find('.task-schedule-params').hide();
- form.find('.task-confirm-btn').css('background-color', '#F32F63');
+ form.find('.task-confirm-btn').removeClass('btn-large-green');
+ form.find('.task-confirm-btn').addClass('btn-large-red');
form.find('.task-confirm-btn').html('Execute now');
}
});
@@ -591,7 +580,7 @@ $(document).on('submit','#task-form',function (e) {
// Controller:
'task',
// Action:
- 'validateForm',
+ 'validate-execute',
// Data:
{
taskParams: taskParamsJson,
@@ -604,8 +593,10 @@ $(document).on('submit','#task-form',function (e) {
mypanel.close();
// Uncheck all checkboxes and remove all styles JQuery could have applied
- $('.reposList').find('input[name=checkbox-repo]').prop('checked', false);
- $('.reposList').find('input[name=checkbox-repo]').removeAttr('style');
+ $('#repositories-list').find('input[name=checkbox-repo]').prop('checked', false);
+ $('#repositories-list').find('input[name=checkbox-repo]').removeAttr('style');
+
+ mycontainer.reload('repos/kpi');
});
return false;
diff --git a/www/public/resources/styles/common.css b/www/public/resources/styles/common.css
index b22f1270b..957d4b87d 100644
--- a/www/public/resources/styles/common.css
+++ b/www/public/resources/styles/common.css
@@ -1,5 +1,5 @@
/**
- * v1.21
+ * v1.22
*/
@font-face{font-family: 'Roboto'; src: url('/assets/fonts/Roboto/Roboto-Regular.ttf') format('truetype');}
@@ -54,7 +54,7 @@ h1, h2, h3, h4 {
h3 {
width: max-content;
font-size: 18px;
- margin: 50px 0 40px 0;
+ margin: 40px 0 40px 0;
border-bottom: 1px solid #15bf7f;
}
@@ -118,7 +118,8 @@ h6 {
padding: 18px;
margin-top: 15px;
margin-bottom: 15px;
- border: 1px dashed #5473e8;
+ /* border: 1px dashed #5473e8; */
+ border: 1px dashed #24405c;
border-radius: 20px;
background-color: #13263a;
}
@@ -181,6 +182,8 @@ pre.codeblock {
.text-right { text-align: right; }
.relative { position: relative; }
.pointer { cursor: pointer; }
+.nopointer{cursor:default!important}
+.notallowed{cursor:not-allowed!important}
.bold { font-weight: bold; }
.greentext{color:#15bf7f !important}
.redtext{color:#F32F63 !important}
@@ -219,6 +222,7 @@ pre.codeblock {
.flex-div-15 { flex: 0 0 14%; }.flex-div-20 { flex: 0 0 19%; }.flex-div-25 { flex: 0 0 24%; }.flex-div-30 { flex: 0 0 29%; }
.flex-div-33 { flex: 0 0 32%; }.flex-div-40 { flex: 0 0 39%; }.flex-div-50 { flex: 0 0 48.5%; }.flex-div-60 { flex: 0 0 59%; }
.flex-div-65 { flex: 0 0 64%; }.flex-div-68 { flex: 0 0 67%; }.flex-div-80 { flex: 0 0 79%; }.flex-div-90 { flex: 0 0 90%; }.flex-div-100 { flex: 0 0 98%; }
+.flex-1{flex:1;}
.flex-grow { flex-grow: 1; }
.flex-wrap { flex-wrap: wrap; }
.flex-nowrap { flex-wrap: nowrap; }
@@ -245,6 +249,7 @@ pre.codeblock {
/* For responsive */
/* column on mobile */
.flexrc{display:flex;flex-direction: column;}
+.flex-wrap-mobile{flex-wrap:wrap;}
.hide-mobile{display:none!important;}
.justify-center {justify-content: center}
.justify-space-between {justify-content: space-between}
@@ -276,10 +281,10 @@ pre.codeblock {
.margin-top-0 {margin-top: 0px !important}.margin-top-5 {margin-top: 5px !important}.margin-top-8{margin-top:8px!important}.margin-top-10 {margin-top: 10px !important}.margin-top-15 {margin-top: 15px !important}.margin-top-20 {margin-top: 20px !important}.margin-top-30 {margin-top: 30px !important}.margin-top-40 {margin-top: 40px !important}.margin-top-50{margin-top:50px !important}
.margin-bottom-0 {margin-bottom: 0px !important}.margin-bottom-5 {margin-bottom: 5px !important}.margin-bottom-8{margin-bottom:8px!important}.margin-bottom-10 {margin-bottom: 10px !important}.margin-bottom-15 {margin-bottom: 15px !important}.margin-bottom-20 {margin-bottom: 20px !important}.margin-bottom-30 {margin-bottom: 30px !important}.margin-bottom-40 {margin-bottom: 40px !important} .margin-bottom-50{margin-bottom:50px!important}
.min-height-100{min-height:100px!important}.min-height-120{min-height:120px!important}.min-height-150{min-height:150px!important}.min-height-200{min-height:200px!important}.min-height-300{min-height:300px!important}.min-height-400{min-height:400px!important}.min-height-500{min-height:500px!important}.min-height-600{min-height:600px!important}.min-height-700{min-height:700px!important}.min-height-800{min-height:800px!important}.min-height-900{min-height:900px!important}.min-height-1000{min-height:1000px!important}
-.min-width-100 {min-width: 100px}.min-width-200 {min-width: 200px}.min-width-300 {min-width: 300px}.min-width-400 {min-width: 400px}.min-width-500 {min-width: 500px}.min-width-600 {min-width: 600px}.min-width-700 {min-width: 700px}.min-width-800 {min-width: 800px}.min-width-900 {min-width: 900px}.min-width-1000 {min-width: 1000px}
+.min-width-100 {min-width: 100px}.min-width-150{min-width: 150px}.min-width-200 {min-width: 200px}.min-width-300 {min-width: 300px}.min-width-400 {min-width: 400px}.min-width-500 {min-width: 500px}.min-width-600 {min-width: 600px}.min-width-700 {min-width: 700px}.min-width-800 {min-width: 800px}.min-width-900 {min-width: 900px}.min-width-1000 {min-width: 1000px}
.min-height-50vh{min-height:50vh}.min-height-90vh{min-height:90vh}.min-height-100vh{min-height:100vh}
.min-width-100vw{min-width:100vw}
-.width-100{width:100%}
+.width-50{width:50%}.width-100{width:100%}
.height-100{height:100%}
.max-width-fit{max-width:fit-content}
.max-width-80{max-width:80px}.max-width-100{max-width:100px}.max-width-200{max-width:200px}.max-width-300{max-width:300px}.max-width-400{max-width:400px}.max-width-500{max-width:500px}.max-width-600{max-width:600px}.max-width-700{max-width:700px}.max-width-800{max-width:800px}.max-width-900{max-width:900px}.max-width-1000{max-width:1000px}
@@ -305,7 +310,7 @@ code {
column-gap: 15px;
padding: 15px;
margin-bottom: 8px;
- border-radius: 20px;
+ border-radius: 18px;
background-color: #182b3e;
box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
}
@@ -318,44 +323,71 @@ code {
grid-template-columns: fit-content(33%) auto 10%;
}
+.table-container-2 {
+ grid-template-columns: auto 10%;
+}
+
+/**
+ * Generic modern list items for slide panels
+ */
+.panel-list-item {
+ padding: 12px 18px;
+ border: 1px solid rgba(52, 97, 136, 0.45);
+ background-color: #223950;
+ transition: border-color 0.2s ease, background 0.2s ease;
+}
+
+.panel-list-item:hover {
+ background-color: #1f3347 !important;
+ border-color: rgba(52, 97, 136, 0.75);
+}
+
[class*=' pagination-btn'],[class^='pagination-btn'] {
display: flex;
align-items: center;
justify-content: center;
- min-width: 35px;
+ min-width: 32px;
height: 30px;
- border: none;
- border-radius: 60px;
- color: white;
+ border: 1px solid #2d5575;
+ border-radius: 6px;
+ color: #97a5b4;
text-align: center;
text-decoration: none;
- font-size: 14px;
+ font-size: 13px;
cursor: pointer;
- background-color: #15bf7f;
- border-radius: 0;
+ background-color: transparent;
+ margin: 0 2px;
+ transition: all 0.15s ease;
}
[class*=' pagination-btn']:hover,[class^='pagination-btn']:hover {
- background-color: #12a16a;
+ background-color: rgba(34, 56, 79, 0.6);
+ border-color: #3d7aab;
+ color: #ffffff;
}
.pagination-btn-current {
- background-color: #12a16a;
- border: 1px solid #10885a;
+ background-color: rgba(21, 191, 127, 0.15);
+ border-color: #15bf7f;
+ color: #15bf7f;
+}
+
+.pagination-btn-current:hover {
+ background-color: rgba(21, 191, 127, 0.25);
+ border-color: #15bf7f;
+ color: #15bf7f;
}
.pagination-btn-first {
- border-top-left-radius: 60px;
- border-bottom-left-radius: 60px;
+ border-radius: 6px;
}
.pagination-btn-last {
- border-top-right-radius: 60px;
- border-bottom-right-radius: 60px;
+ border-radius: 6px;
}
.pagination-btn-previous, .pagination-btn-next {
- width: 40px;
+ width: 35px;
}
.round-item {
@@ -387,6 +419,7 @@ code {
width: 100%;
padding: 20px;
background-color: #182b3e;
+ background-attachment: local;
border-left: 1px solid rgb(117 128 134 / 34%);
box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
overflow: auto;
@@ -448,12 +481,13 @@ code {
.grid-rfr-1-6{grid-template-columns:repeat(6, 1fr)!important}
.grid-rfr-2-4{grid-template-columns:repeat(4, 1fr)!important}
.flexrc{flex-direction:row!important;}
+ .flex-wrap-mobile{flex-wrap:nowrap!important;}
.hide-mobile{display:initial!important;}
.d-align-item-center{align-items:center!important;}
.table-container-3{grid-template-columns: fit-content(10%) auto 10%!important}
.slide-panel {
- width: 40%;
+ width: 47%;
}
#loading {
diff --git a/www/public/resources/styles/components/button.css b/www/public/resources/styles/components/button.css
index 5a3494c1e..48dbacdd6 100644
--- a/www/public/resources/styles/components/button.css
+++ b/www/public/resources/styles/components/button.css
@@ -2,69 +2,110 @@
/* All buttons */
[class^="btn-"], [class*=" btn-"], input::file-selector-button {
padding: 10px;
+ min-height: 34px;
border: none;
border-radius: 60px;
color: white;
text-align: center;
text-decoration: none;
display: inline-block;
+ font-family: 'archivo';
+ font-weight: 600;
font-size: 14px;
+ line-height: 1;
+ letter-spacing: 0.4px;
cursor: pointer;
+ box-sizing: border-box;
box-shadow: rgba(12, 18, 20, 0.504) 0px 0px 5px 1px;
+ transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, opacity 0.2s ease;
}
/* All transparent buttons */
.btn-large-tr, .btn-medium-tr, .btn-small-tr, .btn-xsmall-tr, .btn-xxsmall-tr, .btn-fit-tr, .round-btn-tr,
.btn-large-tr-to-red, .btn-medium-tr-to-red, .btn-small-tr-to-red, .btn-xsmall-tr-to-red, .btn-xxsmall-tr-to-red, .btn-fit-tr-to-red, .round-btn-tr-to-red,
.btn-auto-tr {
- background-color: initial;
- box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
- border: 1px solid #ffffff30;
+ background-color: transparent;
+ box-shadow: none;
+ border: 1.5px solid #ffffff45;
+ color: #cdd7e1;
+}
+
+/* Disabled state for all button classes */
+[class^="btn-"]:disabled,
+[class*=" btn-"]:disabled,
+[class^="btn-"][disabled],
+[class*=" btn-"][disabled] {
+ opacity: 0.45;
+ cursor: not-allowed;
+ pointer-events: none;
}
/* All blue buttons */
.btn-large-blue, .btn-medium-blue, .btn-small-blue, .btn-xsmall-blue, .btn-xxsmall-blue, .btn-fit-blue, .round-btn-blue, .btn-auto-blue {
- background-color: #22384F;
+ background-color: transparent;
+ border: 1.5px solid #5473e8;
+ color: #5473e8;
+ box-shadow: none;
}
/* All blue alt buttons */
.btn-large-blue-alt, .btn-medium-blue-alt, .btn-small-blue-alt, .btn-xsmall-blue-alt, .btn-xxsmall-blue-alt, .btn-fit-blue-alt, .round-btn-blue-alt, .btn-auto-blue-alt {
- background-color: #24405b;
+ background-color: transparent;
+ border: 1.5px solid #5473e8;
+ color: #5473e8;
+ box-shadow: none;
}
/* All green buttons */
.btn-large-green, .btn-medium-green, .btn-small-green, .btn-xsmall-green, .btn-xxsmall-green, .btn-fit-green, .round-btn-green, .btn-auto-green {
- background-color: #15bf7f;
+ /* background-color: rgba(21, 191, 127, 0.15); */
+ background-color: transparent;
+ border: 1.5px solid #15bf7f;
+ color: #15bf7f;
+ box-shadow: none;
}
/* All red buttons */
.btn-large-red, .btn-medium-red, .btn-small-red, .btn-xsmall-red, .btn-xxsmall-red, .btn-fit-red, .round-btn-red, .btn-auto-red {
- background-color: #F32F63;
+ background-color: transparent;
+ border: 1.5px solid #F32F63;
+ color: #F32F63;
+ box-shadow: none;
}
/* All yellow buttons */
.btn-large-yellow, .btn-medium-yellow, .btn-small-yellow, .btn-xsmall-yellow, .btn-xxsmall-yellow, .btn-fit-yellow, .round-btn-yellow, .btn-auto-yellow {
- background-color: #ffb536;
+ background-color: transparent;
+ border: 1.5px solid #ffb536;
+ color: #ffb536;
+ box-shadow: none;
}
/* All auto buttons */
.btn-auto-blue, .btn-auto-blue-alt, .btn-auto-green, .btn-auto-red, .btn-auto-yellow {
width: auto;
}
+/* All fit buttons */
+.btn-fit, .btn-fit-blue, .btn-fit-blue-alt, .btn-fit-green, .btn-fit-red, .btn-fit-yellow, .btn-fit-tr, .btn-fit-tr-to-red {
+ width: fit-content;
+ padding-left: 14px;
+ padding-right: 14px;
+}
+
/* All large buttons */
-.btn-large, .btn-large-blue, .btn-large-green, .btn-large-red, .btn-large-tr {
+.btn-large, .btn-large-blue, .btn-large-blue-alt, .btn-large-green, .btn-large-red, .btn-large-yellow, .btn-large-tr, .btn-large-tr-to-red {
width: 100%;
}
/* All medium buttons */
-.btn-medium, .btn-medium-blue, .btn-medium-green, .btn-medium-red, .btn-medium-tr {
+.btn-medium, .btn-medium-blue, .btn-medium-blue-alt, .btn-medium-green, .btn-medium-red, .btn-medium-yellow, .btn-medium-tr, .btn-medium-tr-to-red {
width: 150px;
}
/* All small buttons */
-.btn-small, .btn-small-blue, .btn-small-green, .btn-small-red, .btn-small-tr {
+.btn-small, .btn-small-blue, .btn-small-blue-alt, .btn-small-green, .btn-small-red, .btn-small-yellow, .btn-small-tr, .btn-small-tr-to-red {
width: 100px;
}
/* All very small buttons */
-.btn-xsmall, .btn-xsmall-blue, .btn-xsmall-green, .btn-xsmall-red, .btn-xsmall-tr {
- width: 50px;
+.btn-xsmall, .btn-xsmall-blue, .btn-xsmall-blue-alt, .btn-xsmall-green, .btn-xsmall-red, .btn-xsmall-yellow, .btn-xsmall-tr, .btn-xsmall-tr-to-red {
+ width: 56px;
}
/* All very very small buttons */
-.btn-xxsmall, .btn-xxsmall-blue, .btn-xxsmall-green, .btn-xxsmall-red, .btn-xxsmall-tr {
+.btn-xxsmall, .btn-xxsmall-blue, .btn-xxsmall-blue-alt, .btn-xxsmall-green, .btn-xxsmall-red, .btn-xxsmall-yellow, .btn-xxsmall-tr, .btn-xxsmall-tr-to-red {
margin: 2px;
width: 34px;
height: 34px;
@@ -72,33 +113,38 @@
/* All transparent buttons (hover) */
.btn-large-tr:hover, .btn-medium-tr:hover, .btn-small-tr:hover, .btn-xsmall-tr:hover, .btn-xxsmall-tr:hover, .btn-fit-tr:hover, .round-btn-tr:hover, .btn-auto-tr:hover {
- transition-duration: 0.2s;
background-color: #22384F;
opacity: 1;
}
/* All transparent to red buttons (hover) */
.btn-large-tr-to-red:hover, .btn-medium-tr-to-red:hover, .btn-small-tr-to-red:hover, .btn-xsmall-tr-to-red:hover, .btn-xxsmall-tr-to-red:hover, .btn-fit-tr-to-red:hover, .round-btn-tr-to-red:hover, .btn-auto-tr-to-red:hover {
- transition-duration: 0.2s;
background-color: #F32F63;
- border: 1px solid #F32F63;
+ border: 1.5px solid #F32F63;
+ color: #ffffff;
opacity: 1;
}
/* All blue buttons (hover) */
-.btn-large-blue:hover, .btn-medium-blue:hover, .btn-small-blue:hover, .btn-xsmall-blue:hover, .btn-xxsmall-blue:hover, .btn-fit-blue:hover, .round-btn-blue:hover, .btn-auto-blue:hover {
- transition-duration: 0.2s;
- background-color: #1e3a54;
+.btn-large-blue:hover, .btn-medium-blue:hover, .btn-small-blue:hover, .btn-xsmall-blue:hover, .btn-xxsmall-blue:hover, .btn-fit-blue:hover, .round-btn-blue:hover, .btn-auto-blue:hover,
+.btn-large-blue-alt:hover, .btn-medium-blue-alt:hover, .btn-small-blue-alt:hover, .btn-xsmall-blue-alt:hover, .btn-xxsmall-blue-alt:hover, .btn-fit-blue-alt:hover, .round-btn-blue-alt:hover, .btn-auto-blue-alt:hover {
+ background-color: rgba(84, 115, 232, 0.15);
+ color: #ffffff;
}
/* All green buttons (hover) */
.btn-large-green:hover, .btn-medium-green:hover, .btn-small-green:hover, .btn-xsmall-green:hover, .btn-xxsmall-green:hover, .btn-fit-green:hover, .round-btn-green:hover, .btn-auto-green:hover {
- transition-duration: 0.2s;
- background-color: #12a16a;
+ background-color: rgba(21, 191, 127, 0.15);
+ color: #ffffff;
}
/* All red buttons (hover) */
.btn-large-red:hover, .btn-medium-red:hover, .btn-small-red:hover, .btn-xsmall-red:hover, .btn-xxsmall-red:hover, .btn-fit-red:hover, .round-btn-red:hover, .btn-auto-red:hover {
- transition-duration: 0.2s;
- background-color: #f10e4b;
+ background-color: rgba(243, 47, 99, 0.15);
+ color: #ffffff;
+}
+/* All yellow buttons (hover) */
+.btn-large-yellow:hover, .btn-medium-yellow:hover, .btn-small-yellow:hover, .btn-xsmall-yellow:hover, .btn-xxsmall-yellow:hover, .btn-fit-yellow:hover, .round-btn-yellow:hover, .btn-auto-yellow:hover {
+ background-color: rgba(255, 181, 54, 0.15);
+ color: #ffffff;
}
/**
@@ -125,6 +171,14 @@
width: 18px;
}
+.round-btn-green {
+ background: rgba(21, 191, 127, 0.22)
+}
+
+.round-btn-green img {
+ filter: brightness(0) saturate(100%) invert(80%) sepia(30%) saturate(400%) hue-rotate(100deg) brightness(110%);
+}
+
/**
* Slide btns
*/
@@ -140,10 +194,10 @@
transition: max-width 0.7s;
margin: 0 3px 0 3px;
color: white;
- /* background-color: #15bf7f; */
border-radius: 60px;
cursor: pointer;
- /* box-shadow: rgba(12, 18, 20, 0.504) 0px 0px 10px 1px; */
+ font-family: 'archivo';
+ font-weight: 500;
}
[class^="slide-btn"] img {
width: 20px;
@@ -168,6 +222,26 @@
.slide-btn-yellow:hover {
background-color: #eb984e;
}
+.slide-btn-green {
+ background-color: rgba(21, 191, 127, 0.22);
+ border: 1px solid rgba(21, 191, 127, 0.8);
+}
+.slide-btn-green span {
+ color: #a0ffd9;
+}
+.slide-btn-green img {
+ filter: brightness(0) saturate(100%) invert(80%) sepia(30%) saturate(400%) hue-rotate(100deg) brightness(110%);
+}
+.slide-btn-green:hover {
+ background-color: rgba(21, 191, 127, 0.38);
+ border-color: #15bf7f;
+}
+.slide-btn-green:hover span {
+ color: #ffffff;
+}
+.slide-btn-green:hover img {
+ filter: brightness(0) invert(1);
+}
.slide-btn-tr, .slide-btn-medium-tr {
background-color: initial;
box-shadow: rgb(32 25 25 / 50%) 0px 0px 15px 1px;
@@ -211,7 +285,8 @@
left: 0;
right: 0;
bottom: 0;
- background-color: #ccc;
+ background-color: rgba(255, 255, 255, 0.06);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
-webkit-transition: .4s;
transition: .4s;
border-radius: 34px;
@@ -223,21 +298,23 @@
width: 16px;
left: 2px;
bottom: 2px;
- background-color: white;
+ background-color: #8b98a5;
-webkit-transition: .4s;
transition: .4s;
border-radius: 50%;
}
.onoff-switch-input:checked + .onoff-switch-slider {
- background-color: #5473e8;
+ background-color: rgba(79, 195, 247, 0.2);
+ box-shadow: inset 0 0 0 1px rgba(79, 195, 247, 0.55);
}
.onoff-switch-input:focus + .onoff-switch-slider {
- box-shadow: 0 0 1px #5473e8;
+ box-shadow: 0 0 0 2px rgba(79, 195, 247, 0.55);
}
.onoff-switch-input:checked + .onoff-switch-slider:before {
-webkit-transform: translateX(15px);
-ms-transform: translateX(15px);
transform: translateX(15px);
+ background-color: #9ab7ff;
}
/**
@@ -264,14 +341,14 @@
.switch-field label,
.single-switch-field label {
- background-color: white;
- color: #3b3b3b;
+ background-color: rgba(255, 255, 255, 0.03);
+ color: #8b98a5;
line-height: 1;
text-align: center;
padding: 6px 8px;
margin-right: -1px;
- border: 1px solid rgba(0, 0, 0, 0.2);
- box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ box-shadow: none;
transition: all 0.1s ease-in-out;
}
@@ -282,9 +359,10 @@
.switch-field input:checked + label,
.single-switch-field input:checked + label {
- background-color: #5473e8;
+ background-color: rgba(79, 195, 247, 0.2);
+ border-color: rgba(79, 195, 247, 0.55);
box-shadow: none;
- color: white;
+ color: #e9f8ff;
}
.switch-field label:first-of-type {
@@ -306,3 +384,20 @@
max-width: 100%;
}
}
+
+/* High visibility overrides for active switches */
+.onoff-switch-input:checked + .onoff-switch-slider {
+ background-color: rgba(79, 195, 247, 0.2) !important;
+ box-shadow: inset 0 0 0 1px rgba(79, 195, 247, 0.55) !important;
+}
+
+.onoff-switch-input:checked + .onoff-switch-slider:before {
+ background-color: #ffffff !important;
+}
+
+.switch-field input:checked + label,
+.single-switch-field input:checked + label {
+ background-color: rgba(79, 195, 247, 0.2) !important;
+ border-color: rgba(79, 195, 247, 0.55) !important;
+ color: #e9f8ff !important;
+}
diff --git a/www/public/resources/styles/components/card.css b/www/public/resources/styles/components/card.css
index a6c3df456..df2c962de 100644
--- a/www/public/resources/styles/components/card.css
+++ b/www/public/resources/styles/components/card.css
@@ -8,6 +8,7 @@
border: 1px solid #24405c;
border-radius: 20px;
box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
+ overflow: hidden;
}
.kpi-value {
@@ -23,24 +24,23 @@
transform: scale(1.10);
}
-.kpi-status-running {
+.kpi-accent-purple {
background: linear-gradient(135deg, #2a0f2e 0%, #4a1942 50%, #2a0f2e 100%);
border-color: #c44daa;
animation: pulse-running 2s ease-in-out infinite;
}
-.kpi-status-done {
+.kpi-accent-green {
background: linear-gradient(135deg, #0f3d2e 0%, #1a5c40 50%, #0f3d2e 100%);
border-color: #1abc6b;
}
-.kpi-status-error,
-.kpi-status-stopped {
+.kpi-accent-red {
background: linear-gradient(135deg, #3d1420 0%, #5c1a2a 50%, #3d1420 100%);
border-color: #c0392b;
}
-.kpi-status-scheduled {
+.kpi-accent-yellow {
background: linear-gradient(135deg, #2d2a0f 0%, #4a4410 50%, #2d2a0f 100%);
border-color: #b8a825;
}
diff --git a/www/public/resources/styles/components/confirmbox.css b/www/public/resources/styles/components/confirmbox.css
index 7c2f16a24..5c71416bc 100644
--- a/www/public/resources/styles/components/confirmbox.css
+++ b/www/public/resources/styles/components/confirmbox.css
@@ -7,20 +7,17 @@
align-items: flex-start;
visibility: hidden;
min-height: 20px;
- padding: 20px 0px 20px 0px;
+ padding: 20px 0px;
font-size: 16px;
position: fixed;
- bottom: 0;
- right: -1000px;
bottom: 50px;
+ right: -1000px;
z-index: 1000;
- background-color: #22384F;
- box-shadow: 0px 0px 5px 0px rgba(0,0,0,1);
+ background-color: #182b3e;
+ border: 1px solid #24405c;
+ border-radius: 20px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
color: white;
- border-radius: 20px 0 0 20px;
- border-left: 5px solid #15bf7f;
- border-top: 1px solid #d1d1d11f;
- border-bottom: 1px solid #d1d1d11f;
}
.confirm-box-cancel-btn {
@@ -30,6 +27,29 @@
cursor: pointer;
}
+.confirm-box .confirm-box-btn {
+ padding: 10px 5px;
+ border-radius: 12px;
+ border: 1px solid #2e5275;
+ background-color: rgba(34, 56, 79, 0.6);
+ box-shadow: none;
+ transition: background-color 0.2s ease, border-color 0.2s ease;
+}
+
+.confirm-box .confirm-box-btn:hover {
+ background-color: rgba(79, 195, 247, 0.12);
+ border-color: rgba(79, 195, 247, 0.4);
+}
+
+.confirm-box .confirm-box-btn.btn-auto-red {
+ border-color: #F32F63;
+}
+
+.confirm-box .confirm-box-btn.btn-auto-red:hover {
+ border-color: #F32F63;
+ background-color: rgba(243, 47, 99, 0.15);
+}
+
/* Desktop configuration */
@media (min-width:1025px) {
.confirm-box {
diff --git a/www/public/resources/styles/components/input.css b/www/public/resources/styles/components/input.css
index f7fd1156c..b078292a3 100644
--- a/www/public/resources/styles/components/input.css
+++ b/www/public/resources/styles/components/input.css
@@ -7,12 +7,18 @@ input[type=text], input[type=date], input[type=time], input[type=number], input[
margin-top: 4px;
margin-bottom: 4px;
padding-left: 10px;
- border: none;
+ border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 20px;
box-sizing: border-box;
font-size: 14px;
color: white;
background-color:#22384F;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+}
+input[type=text]:focus, input[type=date]:focus, input[type=time]:focus, input[type=number]:focus, input[type=email]:focus, input[type=password]:focus, select:focus {
+ outline: none;
+ border-color: rgba(84, 115, 232, 0.8);
+ box-shadow: 0 0 10px rgba(84, 115, 232, 0.25);
}
input[type=file] {
background: none !important;
@@ -133,8 +139,43 @@ textarea {
color: white;
background-color:#22384F;
border-radius: 16px;
- border: none;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+}
+textarea:focus {
+ outline: none;
+ border-color: rgba(84, 115, 232, 0.8);
+ box-shadow: 0 0 10px rgba(84, 115, 232, 0.25);
}
.textarea-100 {
min-height: 100px;
}
+
+/**
+ * Form block
+ * Groups the parameters of a same item (e.g. a repository snapshot) into a distinct section of a form
+ */
+.form-block {
+ padding: 14px 18px 14px 18px;
+ margin-bottom: 24px;
+ background-color: #15273ae3;
+ border: 1px solid #24405c;
+ border-left: 3px solid #24405c;
+ border-radius: 16px;
+}
+
+.form-block > *:first-child {
+ margin-top: 0;
+}
+
+.form-block-accent-red {
+ border-left-color: #F32F63;
+}
+
+.form-block-accent-blue {
+ border-left-color: #5473e8;
+}
+
+.form-block-accent-green {
+ border-left-color: #15bf7f;
+}
diff --git a/www/public/resources/styles/components/label.css b/www/public/resources/styles/components/label.css
index b61b9e1fa..9f11d9d77 100644
--- a/www/public/resources/styles/components/label.css
+++ b/www/public/resources/styles/components/label.css
@@ -1,16 +1,19 @@
[class^="label"] {
+ font-family: 'archivo';
+ font-weight: 600;
font-size: 13px;
+ letter-spacing: 0.2px;
vertical-align: middle;
display: inline-block;
- padding: 6px;
+ padding: 6px 10px;
min-width: 12px;
min-height: 12px;
line-height: 12px !important;
text-align: center;
- border-radius: 60px;
- color: white;
- box-shadow: rgb(12 18 20 / 67%) 0px 0px 0px 1px;
+ border-radius: 20px;
+ background: transparent;
+ box-shadow: none;
}
.label-icon-tr {
display: grid;
@@ -28,29 +31,30 @@
border: 1px solid #24405c;
color: white;
text-align: left;
- box-shadow: rgb(12 18 20 / 67%) 0px 0px 0px 1px;
-}
-.label-black {
- background-color: rgb(46, 54, 58);
- box-shadow: rgb(12 18 20 / 67%) 0px 0px 0px 1px;
+ box-shadow: none;
}
-.label-white {
- background-color: white;
- color: #000000d9;
+
+.label-white, .label-black {
+ color: #c0d0e2;
+ border: 1.5px solid #c0d0e2;
}
.label-green {
- background-color: #15bf7f;
+ color: #15bf7f;
+ border: 1.5px solid #15bf7f;
}
.label-blue {
- background-color: #5473e8;
+ color: #5473e8;
+ border: 1.5px solid #5473e8;
}
.label-red {
- background-color: #F32F63;
+ color: #F32F63;
+ border: 1.5px solid #F32F63;
}
.label-yellow {
- background-color: #ffb536;
+ color: #ffb536;
+ border: 1.5px solid #ffb536;
}
.label-tr {
- background-color: initial;
- border: 1px solid #24405c;
+ color: #8A99AA;
+ border-color: #24405c;
}
diff --git a/www/public/resources/styles/components/layout.css b/www/public/resources/styles/components/layout.css
index c761c141d..1e13f3705 100644
--- a/www/public/resources/styles/components/layout.css
+++ b/www/public/resources/styles/components/layout.css
@@ -4,7 +4,7 @@ body {
min-height: 100vh;
margin: 0px;
padding: 0px;
- background-color: #0e1e2f;
+ background-color: #0e1e30;
color: white;
overflow-y: auto;
font-family: 'Roboto', sans-serif !important;
@@ -12,12 +12,36 @@ body {
}
.div-generic-blue {
- position: relative;
padding: 15px;
margin-bottom: 30px;
border-radius: 20px;
background-color: #182b3e;
box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
+ border: 1px solid #1a4a6a;
+}
+
+.accent-green {
+ border-left: 2px solid #15bf7f !important;
+}
+
+.accent-cyan {
+ border-left: 2px solid #4fc3f7 !important;
+}
+
+.accent-blue {
+ border-left: 2px solid #5473e8 !important;
+}
+
+.accent-yellow {
+ border-left: 2px solid #ffb536 !important;
+}
+
+.accent-orange {
+ border-left: 2px solid #e8851e !important;
+}
+
+.accent-red {
+ border-left: 2px solid #F32F63 !important;
}
.div-generic-blue h3 {
diff --git a/www/public/resources/styles/main.css b/www/public/resources/styles/main.css
index 966eba56e..5c56be3ab 100644
--- a/www/public/resources/styles/main.css
+++ b/www/public/resources/styles/main.css
@@ -1,4 +1,3 @@
-@font-face{font-family: 'ethnocentric'; src: url('/assets/fonts/ethnocentric rg.ttf') format('truetype');}
@font-face{font-family: 'archivo'; src: url('/assets/fonts/Archivo-Variable.ttf') format('truetype');}
.font-family-archivo {
@@ -10,40 +9,20 @@ header {
margin-bottom: 20px;
position: relative;
}
+
#menu {
- width: 100%;
- height: 66px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- flex-wrap: wrap;
- -webkit-box-shadow: 0px 1px 4px 0px rgba(0,0,0,0.75);
- -moz-box-shadow: 0px 1px 4px 0px rgba(0,0,0,0.75);
- box-shadow: 0px 1px 4px 0px rgba(0,0,0,0.75);
-}
-#menu-inline {
display: none;
visibility: hidden;
+ width: 0px;
}
+
#menu-burger {
display: flex;
align-items: center;
justify-content: flex-end;
- padding: 0 20px 0 0;
+ padding: 0 0 20px 0;
}
-#title {
- position: relative;
- margin-left: 20px;
- margin-top: 0px;
- margin-bottom: 0px;
- padding: 0px 25px 0px 0px;
- color: white;
-}
-#title span {
- font-family: 'ethnocentric';
- font-size: 18px;
-}
#devel {
font-size: 8px !important;
position: absolute;
@@ -52,70 +31,11 @@ header {
background-color: #15bf7f;
border-radius: 60px;
}
-[class^="menu-sub-container"] {
- height: 99%;
- display: flex;
- align-items: center;
- margin-left: 25px;
- margin-right: 25px;
-}
-.menu-sub-container a {
- opacity: 0.50;
-}
-.menu-sub-container-underline {
- border-bottom: 1px solid #15bf7f;
- opacity: 1;
-}
-.menu-sub-container:hover a {
- opacity: 1;
-}
-.menu-tab-title {
- display: none;
-}
-#header-refresh {
- position: relative;
-}
-.op-total-running {
- position: absolute;
- right: -15px;
- top: -30px;
- width: 15px;
- height: 15px;
- line-height: 15px !important;
- border: none;
- border-radius: 50%;
- color: white;
- background-color: #F32F63;
- text-align: center;
- font-size: 9px;
- font-family: monospace;
- text-decoration: none;
-}
-.header-op-container {
- display: none;
- position: absolute;
- margin-top: 15px;
- z-index: 99;
-}
-.header-op-subdiv {
- display: flex !important;
- justify-content: space-between;
- min-width: max-content;
- margin-bottom: 3px;
- padding: 8px;
- text-align: left !important;
- align-items: center;
-}
-.header-op-subdiv img {
- height: 18px;
-}
-#header-refresh-container:hover .header-op-container {
- display: block;
-}
+
#notification-count {
position: absolute;
- right: -11px;
- top: -12px;
+ right: -2px;
+ top: -2px;
width: 15px;
height: 15px;
line-height: 15px !important;
@@ -128,20 +48,36 @@ header {
font-family: monospace;
text-decoration: none;
}
+
#userspace {
display: flex;
justify-content: flex-end;
align-items: center;
}
-/* Les articles constituent le container principal des sections section-left et section-right. */
+ /* Wraps the main content and the footer, so that the footer can be pushed to the bottom of the page */
+#main-column {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ min-width: 0;
+}
+
article {
- width: auto;
+ width: 100%;
+ box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
- margin: auto 20px auto 20px;
+ align-content: flex-start;
+ padding: 25px 25px;
}
+
+#virtual-space {
+ display: none;
+ visibility: hidden;
+}
+
.section-left, .section-right {
width: 100%;
vertical-align: top;
@@ -159,18 +95,14 @@ article {
#repos-list-container {
width: 100%;
font-size: 14px;
+ display: flex;
+ flex-direction: column;
+ row-gap: 15px;
+ margin-top: 10px;
}
-.repos-list-group-select-all-btns {
- align-items: center;
- margin-left: 380px;
- column-gap: 8px;
- overflow: auto;
-}
-
-.repos-list-group-select-all-btns input[type="checkbox"] {
- height: 20px;
- width: 20px;
+.repos-list-group:hover img:last-child {
+ display: block;
}
.repos-list-group-flex-div {
@@ -372,170 +304,433 @@ article {
column-gap: 10px;
}
-input::placeholder {
- color: #ffffff9e;
+.repo-description-container {
+ display: flex;
+ align-items: center;
+ column-gap: 8px;
+ margin-top: 5px;
+}
+
+.repo-rename-btn,
+.repo-add-description-btn,
+.repo-add-tags-btn {
+ display: inline-flex;
+ align-items: center;
+ column-gap: 5px;
+ cursor: pointer;
+ /* font-size: 11px; */
+ opacity: 0.5;
+ transition: opacity 0.2s ease;
+}
+
+.repo-rename-btn:hover,
+.repo-add-description-btn:hover,
+.repo-add-tags-btn:hover {
+ opacity: 1;
+}
+
+.repo-description-input.repo-description-editable {
+ cursor: pointer;
+ min-height: 5px;
+}
+
+.repo-tags-display {
+ min-height: 18px;
+}
+
+.repo-tags-display.repo-tags-editable {
+ cursor: pointer;
}
-/* Etiquettes d'environnements */
-.env, .last-env, .env-fit, .last-env-fit {
+.repo-tag-item {
+ cursor: pointer;
+}
+
+.repo-tags-display .repo-tags-input-edit,
+.repo-tags-display .select2-container {
+ min-width: 200px;
+}
+
+/* Task list items */
+.task-item {
+ display: grid;
+ grid-template-columns: 1fr auto auto;
+ align-items: center;
+ column-gap: 20px;
+ padding: 14px 20px;
+ background: linear-gradient(135deg, #121f30, #162a3e);
+ border: 1px solid #1e3a54;
+ border-left: 3px solid #1e3a54;
+ border-radius: 10px;
+ transition: border-color 0.2s ease, background 0.2s ease;
+}
+
+.task-item:hover {
+ border-color: #2d5575;
+ background: linear-gradient(135deg, #152538, #1a2f44);
+}
+
+.task-item-date {
+ font-family: 'archivo';
+ font-size: 14px;
+ font-weight: 500;
+ color: #b8c8d8;
+ letter-spacing: 0.3px;
+}
+
+.task-item-schedule {
+ font-size: 12px;
+}
+
+.task-item-action {
font-size: 13px;
- padding-left: 10px;
- padding-right: 10px;
- padding-top: 4px;
- padding-bottom: 4px;
- vertical-align: middle;
- border-radius: 16px;
}
-.env, .env-fit {
- background-color: white;
- color: #000000d9;
+
+.task-item-repo {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 8px;
}
-.env-fit, .last-env-fit {
- text-align: center;
- display: block;
+
+.task-item-status {
+ display: flex;
+ align-items: center;
+ column-gap: 10px;
}
-.env-fit:hover, .last-env-fit:hover {
- background-color: #e6e6e6;
+
+.task-checkbox-input {
+ display: none;
}
-.last-env, .last-env-fit {
- color: white;
- background-color: #F32F63;
+
+.task-item.task-selected {
+ border-color: #15bf7f;
+ border-left-color: #15bf7f;
+ background: linear-gradient(135deg, #0f2a1f, #132f28);
}
-.last-env:hover, .last-env-fit:hover {
- background-color: #dc0044;
+
+/* Group headers */
+.group-header {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ column-gap: 40px;
+ row-gap: 15px;
+ padding: 0px 20px 14px 5px;
}
-[class^="label-pkg"] {
- display: block;
- width: 19px;
- height: 19px;
- line-height: 19px !important;
- padding: unset;
- text-align: center;
- border-radius: 50%;
- font-size: 7px;
+.group-header-icon {
+ width: 22px;
+ height: 22px;
+}
+
+.group-header-name {
font-family: 'archivo';
- box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
+ font-size: 18px;
+ font-weight: 700;
+ color: #f0f4f8;
+ letter-spacing: 0.3px;
}
-.label-pkg-rpm {
- background-color: #5372e7;
+
+.group-header-count {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 24px;
+ height: 24px;
+ padding: 0 8px;
+ font-family: 'archivo';
+ font-size: 12px;
+ font-weight: 700;
+ color: #4fc3f7;
+ background-color: rgba(79, 195, 247, 0.12);
+ border: 1px solid rgba(79, 195, 247, 0.35);
+ border-radius: 12px;
}
-.label-pkg-deb {
- background-color: #F32F63;
+
+.group-header-right {
+ display: none;
}
-footer {
+.group-header:hover .select-all-btn.hide {
+ display: inline-flex !important;
+}
+
+.select-all-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ min-height: 26px;
+ padding: 0px 12px;
+ opacity: 0.92;
+}
+
+.select-all-btn span {
+ font-size: 13px;
+ font-family: 'archivo';
+ font-weight: 600;
+ line-height: 1;
+ white-space: nowrap;
+ color: inherit;
+}
+
+.select-all-btn input[type="checkbox"] {
+ display: none;
+}
+
+.select-all-btn[status="selected"] {
+ background-color: rgba(21, 191, 127, 0.18);
+ border-color: #15bf7f;
+ color: #15bf7f;
+ opacity: 1;
+}
+
+.repos-list-group-select-latest-btns.hide,
+.repos-list-select-all-btns.hide {
+ display: none !important;
+}
+
+/* Group content wrapper */
+.group-content {
+ margin-left: 15px;
+ padding-left: 22px;
+ border-left: 2px solid #1e3a54;
+}
+
+/* Makes a repo container span the full width of its grid row when it is alone on it */
+.repo-span-full {
+ grid-column: 1 / -1;
+}
+
+/* Without this, a repo wider than its grid column stretches the column instead of letting its content scroll */
+.repo-item-wrapper {
+ min-width: 0;
+}
+
+.repo-item {
+ height: stretch;
display: flex;
flex-direction: column;
- justify-content: space-between;
- row-gap: 40px;
+ min-width: 0;
+}
+
+.repo-snapshots {
+ overflow-x: auto;
+}
+
+/* Mobile / tablet: keep each snapshot on a single line and scroll them horizontally rather than squeezing them */
+@media (max-width: 1024px) {
+ .repo-item-header {
+ flex-wrap: wrap;
+ }
+
+ .repo-snapshots > .snap-container {
+ grid-template-columns: 1fr max-content !important;
+ column-gap: 20px;
+ width: max-content;
+ min-width: 100%;
+ }
+
+ .repo-snapshots .snap-envs {
+ flex-wrap: nowrap;
+ }
+}
+
+.repo-tags-container {
+ margin-top: 5px;
+}
+
+/* Same hover behaviour for the "Add a description" / "Add tags" buttons.
+ The display:none logic (button only present when the field is empty) is kept;
+ this only controls the hover reveal, uniform with the rename button. */
+.repo-item .repo-rename-btn,
+.repo-item .repo-add-description-btn,
+.repo-item .repo-add-tags-btn {
+ visibility: hidden;
+}
+
+.repo-item:hover .repo-rename-btn,
+.repo-item:hover .repo-add-description-btn,
+.repo-item:hover .repo-add-tags-btn {
+ visibility: visible;
+}
+
+.repos-row {
+ align-items: start;
+}
+
+.repo-name {
+ font-family: 'archivo';
+ font-size: 16px;
+ font-weight: 700;
+ letter-spacing: 0.5px;
+}
+
+/* new ui */
+.snap-container {
width: auto;
- height: 300px;
- flex-grow: 1;
- margin-top: 200px;
- padding: 40px 25px 50px 25px;
- box-shadow:
- 0px 1px 1px 0px rgba(0, 0, 0, 0.5) inset,
- 0px 2px 2px 0px rgba(109, 109, 109, 0.2);
- background-color:#182b3e;
+ display: grid;
+ align-items: center;
+ column-gap: 6px;
+ row-gap: 20px;
+ padding: 16px 24px;
+ background: linear-gradient(135deg, #121f30, #162a3e);
+ border: 1px solid #1e3a54;
+ border-radius: 10px;
+ transition: border-color 0.2s ease, background 0.2s ease;
}
-footer #github img { width: 25px; }
+.snap-container:hover {
+ border-color: #2d5575;
+ background: linear-gradient(135deg, #152538, #1a2f44);
+}
-.circle-div-container div {
- display: inline-block;
- vertical-align: middle;
- margin: 5px 10px 5px 0px;
+.snap-separator {
+ width: 1px;
+ height: 24px;
+ background-color: #2a4562;
+ justify-self: center;
}
-.circle-div-container-count, .circle-div-container-count-green, .circle-div-container-count-yellow, .circle-div-container-count-red {
- position: relative;
- border-radius: 50%;
- border: 4px solid #5473e8;
- text-align: center;
- display: inline-block;
- padding: 10px;
- width: 25px;
- height: 25px;
- -webkit-box-shadow: 0px 10px 13px -12px #000000, 0px 0px 10px 2px rgb(0 0 0 / 15%);
- box-shadow: 0px 10px 13px -12px #000000, 0px 0px 10px 2px rgb(0 0 0 / 15%);
+.snap-checkbox {
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
-.circle-div-container-count span, .circle-div-container-count-green span, .circle-div-container-count-yellow span, .circle-div-container-count-red span {
- position: absolute;
- top: 50%;
- left: 50%;
- -ms-transform: translate(-50%, -50%);
- transform: translate(-50%, -50%);
- font-size: 13.5px;
+
+.snap-checkbox input[type="checkbox"] {
+ width: 16px;
+ height: 16px;
+ cursor: pointer;
+}
+
+.snap-checkbox-input {
+ display: none;
+}
+
+.snap-container.snap-selected {
+ border-color: #15bf7f;
+ background: linear-gradient(135deg, #0f2a1f, #132f28);
+}
+
+.snap-icon {
+ width: 18px;
+ height: 18px;
}
-.circle-div-container-count-green { border: 4px solid #15bf7f; }
-.circle-div-container-count-yellow { border: 4px solid #ffb536; }
-.circle-div-container-count-red { border: 4px solid #F32F63; }
-#repo-storage-chart {
- min-height: 18px !important;
- height: 18px !important;
- width: 18px !important;
+.snap-date {
+ font-family: 'archivo';
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 0.3px;
+}
+
+.snap-size {
+ font-size: 12px;
+ letter-spacing: 0.8px;
+}
+
+.snap-signed {
+ font-size: 12px;
+ letter-spacing: 0.8px;
}
-.donut-chart-container {
- width: 120px;
- height: 120px;
+.snap-envs {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ column-gap: 10px;
+ row-gap: 20px;
+}
+
+.snap-env-container {
position: relative;
- margin: auto;
+ cursor: pointer;
+ border-radius: 20px;
+ transition: box-shadow 0.2s ease;
}
-.donut-chart {
- position: absolute;
- z-index: 20;
- display: block;
- width: 100% !important;
- height: 100% !important;
+.snap-env-container .select-env-checkbox {
+ display: none;
}
-.donut-chart-container .donut-legend {
- position: absolute;
- text-align: center;
- font-size: 13px;
- top: 40%;
- left: 50%;
- -ms-transform: translate(-50%, -50%);
- transform: translate(-50%, -50%);
+/* Selected environment */
+.snap-env-container.env-selected .snap-env {
+ background-color: rgba(21, 191, 127, 0.15) !important;
+ border-color: #15bf7f !important;
+ color: #15bf7f !important;
+ box-shadow: 0 0 8px rgba(21, 191, 127, 0.3);
}
-.donut-legend-title {
- position: absolute;
- text-align: center;
+.snap-env-container.env-selected .snap-env span {
+ color: #15bf7f !important;
+}
+.snap-env-container.env-selected .snap-env svg {
+ color: #15bf7f !important;
+}
+
+/* Env labels */
+.env {
+ display: inline-flex !important;
+ width: fit-content;
+ padding: 5px 14px;
+ vertical-align: middle;
+ border-radius: 16px;
+ background: transparent;
+ color: #a0b0c0;
+ border: 1.5px solid currentColor;
+ box-shadow: none;
+ letter-spacing: 0.3px;
+ border-radius: 20px;
+ margin-left: 0;
+}
+
+.env span {
font-size: 13px;
- top: 33%;
- left: 50%;
- -ms-transform: translate(-50%, -50%);
- transform: translate(-50%, -50%);
+ font-family: 'archivo';
+ font-weight: 600;
}
-.donut-legend-content {
- width: 100%;
- position: absolute;
+
+[class^="label-pkg"] {
+ display: block;
+ width: 19px;
+ height: 19px;
+ line-height: 19px !important;
+ padding: unset;
text-align: center;
- font-size: 18px;
- top: 60%;
- left: 50%;
- -ms-transform: translate(-50%, -50%);
- transform: translate(-50%, -50%);
+ border-radius: 50%;
+ font-size: 7px;
+ font-family: 'archivo';
+ box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
}
-
-.profiles-container {
- columns: 1;
- column-count: 1;
- width: 100%;
+.label-pkg-rpm {
+ background-color: #5372e7;
+}
+.label-pkg-deb {
+ background-color: #F32F63;
}
-.profiles-container > div {
- break-inside: avoid;
+footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ row-gap: 20px;
+ width: 100%;
+ box-sizing: border-box;
+ margin-top: auto;
+ padding: 50px 25px 30px 25px;
}
.header-blue, .header-blue-min { background-color: #22384F; }
.header-light-red, .header-light-red-min { background-color: #ff3369 !important; }
.header-light-blue, .header-light-blue-min { background-color: #2b4d6e !important; }
+/* Disable scroll when maintenance mode is active */
+body:has(#maintenance-container) {
+ overflow: hidden;
+}
+
#maintenance-container {
position: fixed;
top: 0;
@@ -577,30 +772,227 @@ footer #github img { width: 25px; }
/* Desktop configuration */
@media (min-width:1500px) {
- #menu {
- justify-content: flex-start;
+ #menu-burger {
+ display: none;
}
- #menu-inline {
- height: 100%;
+
+ #menu
+ {
+ display: flex !important;
+ visibility: visible !important;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-between;
+ row-gap: 10px;
+ width: 60px;
+ height: 100vh;
+ }
+
+ #menu-fixed {
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 100vh;
+ max-height: 100vh;
+ width: 60px;
display: flex;
- visibility: visible;
+ flex-direction: column;
+ align-items: center;
justify-content: space-between;
- flex-grow: 1;
+ row-gap: 10px;
+ background-color: #182b3e;
+ border-right: 1px solid #24405c;
+ padding: 20px 0px;
+ box-sizing: border-box;
+ z-index: 500;
}
- #menu-burger {
- display: none !important;
- visibility: hidden !important;
+
+ #menu img {
+ width: 24px;
+ height: 24px;
+ }
+
+ #menu .menu-sub-item img {
+ width: 20px;
+ height: 20px;
+ }
+
+ /* Menu items hover & active state */
+ .menu-item,
+ .menu-sub-item {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 40px;
+ height: 40px;
+ border-radius: 12px;
+ transition: background-color 0.2s ease, transform 0.15s ease;
+ }
+
+ .menu-item > a,
+ .menu-sub-item > a{
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ border-radius: inherit;
+ }
+
+ .menu-item img,
+ .menu-sub-item img {
+ opacity: 0.55;
+ transition: opacity 0.2s ease, filter 0.2s ease;
+ }
+
+ .menu-item:hover,
+ .menu-sub-item:hover {
+ background-color: rgba(79, 195, 247, 0.08);
+ transform: scale(1.08);
+ }
+
+ .menu-item-tasks:hover {
+ transform: none;
+ }
+
+ .menu-item:hover img {
+ opacity: 1;
+ filter: drop-shadow(0 0 3px rgba(79, 195, 247, 0.4));
+ }
+
+ .menu-item-active {
+ background-color: rgba(21, 191, 127, 0.12);
+ position: relative;
+ }
+
+ .menu-item-active::before {
+ content: '';
+ position: absolute;
+ left: -10px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 3px;
+ height: 24px;
+ background-color: #15bf7f;
+ border-radius: 0 3px 3px 0;
+ }
+
+ .menu-item-active img {
+ opacity: 1;
+ filter: brightness(1.2) drop-shadow(0 0 2px rgba(21, 191, 127, 0.3));
+ }
+
+ .menu-item-active:hover {
+ background-color: rgba(21, 191, 127, 0.16);
}
- .menu-tab-title {
+
+ /* Running tasks badge on sidebar menu */
+ .menu-item-tasks {
+ position: relative;
+ }
+
+ .menu-task-badge {
+ position: absolute;
+ top: -4px;
+ right: -4px;
+ width: 16px;
+ height: 16px;
+ line-height: 16px !important;
+ border-radius: 50%;
+ background-color: #F32F63;
+ color: white;
+ text-align: center;
+ font-size: 9px;
+ font-family: monospace;
+ pointer-events: none;
+ z-index: 2;
+ }
+
+ .menu-task-popup {
+ display: none;
+ position: absolute;
+ left: calc(100% + 15px);
+ top: 0;
+ min-width: max-content;
+ max-height: 80vh;
+ overflow-y: auto;
+ background-color: #182b3e;
+ border: 1px solid #24405c;
+ border-radius: 12px;
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
+ padding: 6px;
+ z-index: 400;
+ }
+
+ /* Bridges the gap between the menu item and the popup, so moving the mouse to the popup does not close it.
+ It cannot be carried by the popup itself, whose overflow-y would clip it. */
+ .menu-item-tasks::after {
+ content: '';
+ display: none;
+ position: absolute;
+ left: 100%;
+ top: 0;
+ width: 15px;
+ height: 100%;
+ }
+
+ .menu-item-tasks:hover::after,
+ .menu-item-tasks:hover .menu-task-popup {
display: block;
- font-family: 'Archivo';
- font-weight: 600;
- font-size: 16px;
+ }
+
+ .menu-task-popup-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ column-gap: 14px;
+ padding: 8px 12px;
+ border-radius: 8px;
+ white-space: nowrap;
+ transition: background-color 0.15s ease;
+ }
+
+ .menu-task-popup-item:hover {
+ background-color: rgba(79, 195, 247, 0.08);
+ font-weight: normal;
+ }
+
+ .menu-task-popup-item-link {
+ display: flex;
+ align-items: center;
+ column-gap: 8px;
+ }
+
+ .menu-task-popup-item-link span:first-child {
+ color: #97a5b4;
+ font-size: 13px;
+ }
+
+ .menu-task-popup-item .stop-task-btn {
+ display: flex;
+ align-items: center;
+ opacity: 0;
+ transition: opacity 0.15s ease;
+ }
+
+ .menu-task-popup-item:hover .stop-task-btn {
+ opacity: 1;
}
article {
flex-direction: row;
flex-wrap: wrap;
+ padding: 10px 0;
+ }
+
+ footer {
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ #virtual-space {
+ display: block;
+ visibility: visible;
}
.section-main {
@@ -623,4 +1015,16 @@ footer #github img { width: 25px; }
.repos-list-group-flex-div {
grid-template-columns: 350px 55px auto auto auto 55px 1fr 40px;
}
+
+ /* .toolbar {
+ flex-wrap: nowrap;
+ }
+
+ .toolbar #input {
+ min-width: 150px;
+ } */
+
+ .snap-envs {
+ justify-content: flex-end;
+ }
}
\ No newline at end of file
diff --git a/www/public/resources/styles/select2.css b/www/public/resources/styles/select2.css
index 9229e9a7e..1cf616165 100644
--- a/www/public/resources/styles/select2.css
+++ b/www/public/resources/styles/select2.css
@@ -154,8 +154,9 @@ https://github.com/select2/select2/issues/3278#issuecomment-258481272 : */
.select2-container--default .select2-selection--single {
background-color: #22384F; /* modifié */
/*background-color: #fff;*/
- /* border: 1px solid #aaa; */
- border-radius: 20px; }
+ border: 1px solid rgba(255, 255, 255, 0.08); /* modifié */
+ border-radius: 20px;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease; }
.select2-container--default .select2-selection--single .select2-selection__rendered {
/*color: #444;*/
line-height: 28px; }
@@ -207,13 +208,15 @@ https://github.com/select2/select2/issues/3278#issuecomment-258481272 : */
.select2-container--default .select2-selection--multiple {
background-color: #22384F; /* modifié */
+ border: 1px solid rgba(255, 255, 255, 0.08); /* modifié */
border-radius: 20px;
cursor: text;
padding-bottom: 5px;
- padding-left: 10px;
+ padding-left: 2px;
padding-right: 5px;
height: 100%;
min-height: 35px;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
position: relative; }
.select2-container--default .select2-selection--multiple.select2-selection--clearable {
padding-right: 25px; }
@@ -227,7 +230,10 @@ https://github.com/select2/select2/issues/3278#issuecomment-258481272 : */
right: 0;
padding: 1px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice {
- background-color: #5473e8; /* modifié */
+ background-color: rgba(79, 195, 247, 0.2); /* modifié */
+ border: 1px solid rgba(79, 195, 247, 0.55); /* modifié */
+ color: #e9f8ff; /* modifié */
+ box-shadow: none; /* modifié */
border-radius: 16px;
box-sizing: border-box;
display: inline-block;
@@ -242,9 +248,11 @@ https://github.com/select2/select2/issues/3278#issuecomment-258481272 : */
overflow: hidden;
text-overflow: ellipsis;
vertical-align: bottom;
+ transition: background-color 0.15s ease, border-color 0.15s ease;
white-space: nowrap; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__display {
cursor: default;
+ color: #e9f8ff;
padding-left: 2px;
padding-right: 5px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
@@ -253,7 +261,7 @@ https://github.com/select2/select2/issues/3278#issuecomment-258481272 : */
/* border-right: 1px solid #aaa; */
border-top-left-radius: 16px;
border-bottom-left-radius: 16px;
- color: #999;
+ color: #e9f8ff; /* modifié */
cursor: pointer;
font-size: 1em;
font-weight: bold;
@@ -264,8 +272,8 @@ https://github.com/select2/select2/issues/3278#issuecomment-258481272 : */
left: 0;
top: 0; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover, .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:focus {
- background-color: #F32F63;
- color: #333;
+ background-color: rgba(243, 47, 99, 0.9); /* modifié */
+ color: #ffffff; /* modifié */
outline: none; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
@@ -289,8 +297,12 @@ https://github.com/select2/select2/issues/3278#issuecomment-258481272 : */
margin-left: 10px;
margin-right: auto; }
-.select2-container--default.select2-container--focus .select2-selection--multiple {
- outline: 0; }
+.select2-container--default.select2-container--focus .select2-selection--multiple,
+.select2-container--default.select2-container--open .select2-selection--single,
+.select2-container--default.select2-container--open .select2-selection--multiple {
+ outline: 0;
+ border-color: rgba(84, 115, 232, 0.8);
+ box-shadow: 0 0 10px rgba(84, 115, 232, 0.25); }
.select2-container--default.select2-container--disabled .select2-selection--multiple {
background-color: #eee;
@@ -349,12 +361,13 @@ https://github.com/select2/select2/issues/3278#issuecomment-258481272 : */
color: #999; }
.select2-container--default .select2-results__option--selected {
- background-color: #596EB5; /* modifié */
+ background-color: rgba(79, 195, 247, 0.2); /* modifié */
+ color: #e9f8ff;
}
.select2-container--default .select2-results__option--highlighted.select2-results__option--selectable {
- background-color: #596EB5; /* modifié */
- color: white; }
+ background-color: rgba(79, 195, 247, 0.2); /* modifié */
+ color: #e9f8ff; }
.select2-container--default .select2-results__group {
cursor: default;
@@ -561,4 +574,16 @@ https://github.com/select2/select2/issues/3278#issuecomment-258481272 : */
padding: 6px; }
.select2-container--classic.select2-container--open .select2-dropdown {
- border-color: #5897fb; }
\ No newline at end of file
+ border-color: #5897fb; }
+
+/* High visibility overrides for selected tags */
+.select2-container--default .select2-selection--multiple .select2-selection__choice {
+ background-color: rgba(79, 195, 247, 0.2) !important;
+ border: 1px solid rgba(79, 195, 247, 0.55) !important;
+ color: #e9f8ff !important;
+}
+
+.select2-container--default .select2-selection--multiple .select2-selection__choice__display,
+.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
+ color: #e9f8ff !important;
+}
\ No newline at end of file
diff --git a/www/public/resources/styles/browse.css b/www/public/resources/styles/snapshot.css
similarity index 95%
rename from www/public/resources/styles/browse.css
rename to www/public/resources/styles/snapshot.css
index 5ca4ec2fd..1fa953e51 100644
--- a/www/public/resources/styles/browse.css
+++ b/www/public/resources/styles/snapshot.css
@@ -1,6 +1,3 @@
-/**
-* Explorateur de fichiers
-*/
#explorer,
#browse-search-results {
font-size: 14px;
diff --git a/www/public/resources/styles/stats-hosts.css b/www/public/resources/styles/stats-hosts.css
index 76956fa99..fd70425e0 100644
--- a/www/public/resources/styles/stats-hosts.css
+++ b/www/public/resources/styles/stats-hosts.css
@@ -20,13 +20,6 @@
column-gap: 15px;
}
-.host-available-packages-label {
- min-width: 20px;
- text-align: center;
- border-radius: 60px;
- padding: 1px 2px;
-}
-
.host-additionnal-info {
display: none;
flex-direction: column;
@@ -62,13 +55,136 @@
-webkit-column-width: 30%;
column-count: 1;
width: 100%;
- column-gap: 1%;
+ column-gap: 50px;
}
.hosts-group-container {
break-inside: avoid;
}
+/* Host group header (aligned with repos group-header style) */
+.hosts-group-header {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ column-gap: 40px;
+ row-gap: 15px;
+ padding: 0px 20px 14px 5px;
+}
+
+.hosts-group-header:hover .select-group-hosts-btn.hide {
+ display: inline-flex !important;
+}
+
+.select-group-hosts-btn.hide {
+ display: none !important;
+}
+
+.hosts-group-header-left {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.hosts-group-header-name {
+ font-family: 'archivo';
+ font-size: 18px;
+ font-weight: 700;
+ color: #f0f4f8;
+ letter-spacing: 0.3px;
+}
+
+.hosts-group-header-count {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 24px;
+ height: 24px;
+ padding: 0 8px;
+ font-family: 'archivo';
+ font-size: 12px;
+ font-weight: 700;
+ color: #4fc3f7;
+ background-color: rgba(79, 195, 247, 0.12);
+ border: 1px solid rgba(79, 195, 247, 0.35);
+ border-radius: 12px;
+}
+
+/* Host group content (aligned with repos group-content style) */
+.hosts-group-content {
+ margin-left: 12px;
+ padding-left: 20px;
+ border-left: 2px solid #1e3a54;
+}
+
+/* Host line item (aligned with task-item / snap-container style) */
+.host-line {
+ display: grid;
+ grid-template-columns: 1fr;
+ align-items: center;
+ column-gap: 20px;
+ padding: 14px 20px;
+ border-radius: 10px;
+ border-left: 3px solid #1e3a54;
+ cursor: pointer;
+ transition: border-color 0.2s ease, background 0.2s ease;
+}
+
+.host-line:hover {
+ border-color: #2d5575;
+ background: linear-gradient(135deg, #152538, #1a2f44);
+}
+
+.host-line.host-selected {
+ border-color: #15bf7f;
+ background: linear-gradient(135deg, #0f2a1f, #132f28);
+}
+
+.host-line .js-host-checkbox {
+ display: none;
+}
+
+.host-accent-green {
+ border-left-color: #15bf7f;
+}
+
+.host-accent-red {
+ border-left-color: #F32F63;
+}
+
+.host-accent-yellow {
+ border-left-color: #ffb536;
+}
+
+.searchInput-container {
+ width: 100%;
+ margin-bottom: 50px;
+}
+
+.searchInput-subcontainer {
+ margin: auto;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ column-gap: 40px;
+}
+
+.searchInput-subcontainer p {
+ text-align: center;
+}
+
+.hosts-charts-container {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ margin: 0 auto 20px auto;
+}
+
+.hosts-chart-sub-container {
+ min-height: 230px;
+ position: relative;
+}
+
.hosts-charts-list-label > div > span:first-child {
display: inline-block;
width: 10px;
@@ -214,6 +330,16 @@
justify-content: space-evenly;
}
+ .hosts-charts-container {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 20px;
+ }
+
+ .hosts-chart-wide {
+ grid-column: span 2;
+ }
+
.event-packages-details {
min-width: 50vw;
max-width: 90vw;
@@ -221,4 +347,79 @@
column-gap: 10px;
row-gap: 15px;
}
+}
+
+/**
+ * Host detail page items (packages, events, requests)
+ */
+.host-package-item,
+.host-event-item,
+.host-request-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 12px 18px;
+ background: linear-gradient(135deg, #121f30, #162a3e);
+ border: 1px solid #1e3a54;
+ border-left: 3px solid #1e3a54;
+ border-radius: 10px;
+ transition: border-color 0.2s ease, background 0.2s ease;
+}
+
+.host-package-item:hover,
+.host-event-item:hover,
+.host-request-item:hover {
+ border-color: #2d5575;
+ background: linear-gradient(135deg, #152538, #1a2f44);
+}
+
+/* Selectable packages */
+.host-package-item.pointer {
+ cursor: pointer;
+}
+
+.host-package-item .available-package-checkbox {
+ display: none;
+}
+
+.host-package-item.host-package-selected {
+ border-color: #15bf7f;
+ background: linear-gradient(135deg, #0f2a1f, #132f28);
+}
+
+.host-package-item.host-package-excluded {
+ position: relative;
+ border-left-color: #d4a574;
+ opacity: 0.78;
+}
+
+.host-package-item.host-package-excluded::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: repeating-linear-gradient(
+ 45deg,
+ transparent,
+ transparent 10px,
+ rgba(212, 165, 116, 0.15) 10px,
+ rgba(212, 165, 116, 0.15) 20px
+ );
+ border-radius: 10px;
+ pointer-events: none;
+}
+
+/* Request status accents */
+.host-request-item.request-accent-green {
+ border-left-color: #15bf7f;
+}
+
+.host-request-item.request-accent-red {
+ border-left-color: #F32F63;
+}
+
+.host-request-item.request-accent-yellow {
+ border-left-color: #ffb536;
}
\ No newline at end of file
diff --git a/www/public/resources/styles/run.css b/www/public/resources/styles/tasks.css
similarity index 53%
rename from www/public/resources/styles/run.css
rename to www/public/resources/styles/tasks.css
index 3fdf5d64f..bd53beaff 100644
--- a/www/public/resources/styles/run.css
+++ b/www/public/resources/styles/tasks.css
@@ -1,20 +1,24 @@
-#log-refresh-container {
+#task-refresh-container {
flex: 0 0 100%;
position: relative;
min-height: 90vh;
}
-#log-refresh-container h3 {
+#task-refresh-container h3 {
margin-top: 0 !important;
margin-bottom: 0 !important;
border: none;
}
-#log-refresh-container h6 {
+#task-refresh-container #task-details h6 {
+ margin-bottom: 5px;
+}
+
+#task-refresh-container h6 {
margin-top: 30px;
}
-#log-refresh-container pre { /* empecher le contenu des de dépasser */
+#task-refresh-container pre {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
@@ -27,6 +31,7 @@
margin: 10px 0 0 0;
}
+/* Legacy log layout, to be removed */
.getPackagesDiv, .signRepoDiv, .createRepoDiv {
background-color: #182b3e;
padding: 15px;
@@ -39,26 +44,47 @@
line-height: 2 !important;
}
-/* Boutons Top et Down pour atteindre le haut ou le bas de page */
#scroll-btns-container {
- display: none;
+ display: block;
+ position: fixed;
+ right: 20px;
+ bottom: 20px;
+ z-index: 1000;
}
-/* Les boutons scroll sont fixes à l'intérieur du container */
+
#scroll-btns {
- position: sticky;
- top: 570px;
display: flex;
flex-direction: column;
row-gap: 5px;
}
+#autoscroll-btn {
+ width: 40px;
+ height: 40px;
+}
+
+#scroll-btns-container .round-btn-icon {
+ display: block;
+ width: 20px;
+ height: 20px;
+ background-color: currentColor;
+ -webkit-mask: var(--icon) center / contain no-repeat;
+ mask: var(--icon) center / contain no-repeat;
+}
+
+/* Force the icon to follow the accent color (round-btn base sets color:white) */
+#scroll-btns-container .round-btn-red { color: #F32F63; }
+#scroll-btns-container .round-btn-yellow { color: #ffb536; }
+#scroll-btns-container .round-btn-green { color: #15bf7f; }
+#scroll-btns-container [class^="round-btn"]:hover { color: #ffffff; }
+
.op-header-container {
margin-top: 4px;
margin-bottom: 4px;
margin-left: 15px;
}
-/* Legacy task log layout */
+/* Legacy task log layout, to be removed */
.op-table {
width: 100%;
margin: 5px;
@@ -111,12 +137,49 @@
.task-step {
display: flex;
flex-direction: column;
- margin-top: 5px;
+ margin-top: 8px;
border-radius: 10px;
- border: 1px solid #24405c;
- background-color: #182b3e;
- -webkit-box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
- box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
+ border: 1px solid #1e3a54;
+ border-left: 3px solid #1e3a54;
+ background: linear-gradient(135deg, #121f30, #162a3e);
+ transition: border-color 0.2s ease, background 0.2s ease;
+}
+
+.task-step[status="completed"] {
+ border-left-color: #15bf7f;
+ background: linear-gradient(135deg, rgba(21, 191, 127, 0.12), rgba(21, 191, 127, 0.03));
+ border-color: rgba(21, 191, 127, 0.3);
+}
+
+.task-step[status="error"],
+.task-step[status="stopped"] {
+ border-left-color: #F32F63;
+ background: linear-gradient(135deg, rgba(243, 47, 99, 0.12), rgba(243, 47, 99, 0.03));
+ border-color: rgba(243, 47, 99, 0.3);
+}
+
+.task-step[status="warning"] {
+ border-left-color: #ffb536;
+ background: linear-gradient(135deg, rgba(255, 181, 54, 0.12), rgba(255, 181, 54, 0.03));
+ border-color: rgba(255, 181, 54, 0.3);
+}
+
+.task-step[status="running"] {
+ border: 1px solid #1a4a6a;
+ background: #182b3e;
+}
+
+.task-step[status="completed"] .task-step-status p {
+ color: #15bf7f;
+}
+
+.task-step[status="error"] .task-step-status p,
+.task-step[status="stopped"] .task-step-status p {
+ color: #F32F63;
+}
+
+.task-step[status="warning"] .task-step-status p {
+ color: #ffb536;
}
.task-step-title {
@@ -125,8 +188,9 @@
justify-content: space-between;
align-items: center;
column-gap: 10px;
- padding: 20px;
+ padding: 16px 20px;
border-radius: 10px;
+ min-height: 40px;
}
.task-step-content {
@@ -135,26 +199,36 @@
min-height: 250px;
max-height: 700px;
overflow-y: auto;
- border-radius: 20px;
- margin-top: 4px;
- border-left: 1px solid #24405c;
- border-right: 1px solid #24405c;
- border-bottom: 1px solid #24405c;
- background-color: #182b3e;
- -webkit-box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
- box-shadow: rgb(0 0 0) 0px 10px 13px -12px, rgb(0 0 0 / 15%) 0px 0px 10px 2px;
+ border-radius: 0 0 10px 10px;
+ margin-top: 0;
+ border: 1px solid #1e3a54;
+ border-top: none;
+ background: linear-gradient(180deg, #0e1a28, #121f30);
}
.task-sub-step-container {
- border-right: 1px solid #24405c;
+ border-right: 1px solid #1e3a54;
}
.task-sub-step-content, .step-loading {
display: grid;
grid-template-columns: minmax(20px, 20px) auto;
- column-gap: 10px;
- border-top: 1px solid #24405c;
- padding: 15px;
+ column-gap: 12px;
+ border-top: 1px solid rgba(30, 58, 84, 0.5);
+ padding: 14px 18px;
+ transition: background 0.15s ease;
+}
+
+.task-sub-step-content:first-child {
+ border-top: none;
+}
+
+.task-sub-step-content:hover {
+ background: rgba(255, 255, 255, 0.02);
+}
+
+.task-step.step-open {
+ border-radius: 10px 10px 0 0;
}
.step-content-btns {
@@ -169,15 +243,10 @@
/* Desktop configuration */
@media (min-width:1500px) {
- #log-refresh-container {
+ #task-refresh-container {
flex: 0 0 94%;
}
- #scroll-btns-container {
- display: block;
- order: 2;
- }
-
.task-step-content {
grid-template-columns: 96% 4%;
}
diff --git a/www/update/database/6.0.0.php b/www/update/database/6.0.0.php
new file mode 100644
index 000000000..f34900ca8
--- /dev/null
+++ b/www/update/database/6.0.0.php
@@ -0,0 +1,129 @@
+getMessage());
+}
+
+// Drop some indexes if exists
+try {
+ $this->db->exec("DROP INDEX IF EXISTS repos_env_index");
+ $this->db->exec("DROP INDEX IF EXISTS repos_env_id_snap_index");
+} catch (Exception $e) {
+ throw new Exception('could not delete old indexes from database: ' . $e->getMessage());
+}
+
+// Delete Pkg_translation column from repos_snap database
+if ($this->db->columnExist('repos_snap', 'Pkg_translation')) {
+ try {
+ $this->db->exec("ALTER TABLE repos_snap DROP COLUMN Pkg_translation");
+ } catch (Exception $e) {
+ throw new Exception('could not delete Pkg_translation column from repos_snap table');
+ }
+}
+
+// Add Description column to repos table
+if (!$this->db->columnExist('repos', 'Description')) {
+ try {
+ $this->db->exec("ALTER TABLE repos ADD COLUMN Description VARCHAR(255)");
+ } catch (Exception $e) {
+ throw new Exception('could not add Description column to repos table');
+ }
+}
+
+// Add Tags column to repos table
+if (!$this->db->columnExist('repos', 'Tags')) {
+ try {
+ $this->db->exec("ALTER TABLE repos ADD COLUMN Tags VARCHAR(255)");
+ } catch (Exception $e) {
+ throw new Exception('could not add Tags column to repos table');
+ }
+}
+
+// Delete Description column from repos_env table
+if ($this->db->columnExist('repos_env', 'Description')) {
+ try {
+ $this->db->exec("ALTER TABLE repos_env DROP COLUMN Description");
+ } catch (Exception $e) {
+ throw new Exception('could not delete Description column from repos_env table');
+ }
+}
+
+// Add 'Protected' column to env table
+if (!$this->db->columnExist('env', 'Protected')) {
+ try {
+ $this->db->exec("ALTER TABLE env ADD COLUMN Protected CHAR(5) DEFAULT 'false'");
+ } catch (Exception $e) {
+ throw new Exception('could not add Protected column to env table');
+ }
+}
+
+// Add 'Parent_task_id' column to tasks table
+if (!$this->db->columnExist('tasks', 'Parent_task_id')) {
+ try {
+ $this->db->exec("ALTER TABLE tasks ADD COLUMN Parent_task_id INTEGER");
+ } catch (Exception $e) {
+ throw new Exception('could not add Parent_task_id column to tasks table');
+ }
+}
+
+// Create index on 'Parent_task_id' column
+try {
+ $this->db->exec("CREATE INDEX IF NOT EXISTS tasks_parent_task_id ON tasks (Parent_task_id)");
+} catch (Exception $e) {
+ throw new Exception('could not create tasks_parent_task_id index: ' . $e->getMessage());
+}
+
+// Add 'compliance_security_update' column to settings table
+if (!$hostsDb->columnExist('settings', 'compliance_security_update')) {
+ try {
+ $hostsDb->exec("ALTER TABLE settings ADD COLUMN compliance_security_update INTEGER NOT NULL DEFAULT 1");
+ } catch (Exception $e) {
+ throw new Exception('could not add compliance_security_update column to settings table');
+ }
+}
+
+// Add some columns to hosts database
+try {
+ $result = $hostsDb->query("SELECT Id FROM hosts");
+ $hostIds = [];
+
+ while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
+ $hostIds[] = $row['Id'];
+ }
+
+ foreach ($hostIds as $hostId) {
+ try {
+ $hostDb = new \Models\Connection('host', $hostId);
+
+ // Add 'Security' column to packages table
+ if (!$hostDb->columnExist('packages', 'Security')) {
+ $hostDb->exec("ALTER TABLE packages ADD COLUMN Security CHAR(5)");
+ }
+
+ // Add 'Security' column to packages_history table
+ if (!$hostDb->columnExist('packages_history', 'Security')) {
+ $hostDb->exec("ALTER TABLE packages_history ADD COLUMN Security CHAR(5)");
+ }
+
+ // Add 'Security' column to packages_available table
+ if (!$hostDb->columnExist('packages_available', 'Security')) {
+ $hostDb->exec("ALTER TABLE packages_available ADD COLUMN Security CHAR(5)");
+ }
+ } catch (Exception $e) {
+ // If it fails for a specific host, just drop its database to reset it
+ if (file_exists(HOSTS_DIR . '/' . $hostId . '/properties.db')) {
+ if (!unlink(HOSTS_DIR . '/' . $hostId . '/properties.db')) {
+ throw new Exception('could not reset host #' . $hostId . ' database: ' . $e->getMessage());
+ }
+ }
+ }
+ }
+} catch (Exception $e) {
+ throw new Exception('could not update host databases: ' . $e->getMessage());
+}
diff --git a/www/update/database/ci/deb/rename.php b/www/update/database/ci/deb/rename.php
index a6d0622ca..f8490ab2d 100644
--- a/www/update/database/ci/deb/rename.php
+++ b/www/update/database/ci/deb/rename.php
@@ -3,7 +3,7 @@
* Rename deb repo
*/
$rawParams['action'] = 'rename';
-$rawParams['snap-id'] = '1';
+$rawParams['repo-id'] = '1';
$rawParams['env-id'] = '';
$rawParams['name'] = 'debian-rename';
$rawParams['arch'] = ['amd64', 'armhf'];
diff --git a/www/update/database/ci/rpm/rename.php b/www/update/database/ci/rpm/rename.php
index 1c7f584b7..de0fc42cb 100644
--- a/www/update/database/ci/rpm/rename.php
+++ b/www/update/database/ci/rpm/rename.php
@@ -3,7 +3,7 @@
* Rename rpm repo
*/
$rawParams['action'] = 'rename';
-$rawParams['snap-id'] = '1';
+$rawParams['repo-id'] = '1';
$rawParams['env-id'] = '';
$rawParams['name'] = 'centos-extras-common-rename';
$rawParams['arch'] = ['x86_64', 'noarch'];
diff --git a/www/version b/www/version
index 5d4f567eb..f4965a313 100644
--- a/www/version
+++ b/www/version
@@ -1 +1 @@
-5.14.0
\ No newline at end of file
+6.0.0
\ No newline at end of file
diff --git a/www/views/includes/containers/browse/actions.inc.php b/www/views/includes/containers/browse/actions.inc.php
deleted file mode 100644
index 743c5b17c..000000000
--- a/www/views/includes/containers/browse/actions.inc.php
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
- UPLOAD PACKAGES
-
-
-
-
TASK RUNNING
-
-

-
A task is running on this repository snapshot.
-
-
-
-
-
-
-
- ' . $uploadSuccessMessage . '';
- }
-
- // Print error messages from uploading packages if there are
- if (!empty($uploadErrorDetails)) {
- foreach ($uploadErrorDetails as $errorTitle => $errorPackages) {
- echo '
' . htmlspecialchars($errorTitle) . ':
';
- foreach ($errorPackages as $pkg) {
- echo '
' . htmlspecialchars($pkg) . '
';
- }
- }
- }
-
- if (!empty($uploadErrorMessage)) {
- echo '
' . $uploadErrorMessage . '
';
- } ?>
-
-
- ';
- // echo $e->getMessage();
- // echo '