diff --git a/core/imageroot/usr/local/agent/actions/set-note/50set_note b/core/imageroot/usr/local/agent/actions/set-note/50set_note new file mode 100755 index 000000000..6c274ee4c --- /dev/null +++ b/core/imageroot/usr/local/agent/actions/set-note/50set_note @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2025 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import json +import sys +import agent +import os + +agent_id = os.environ['AGENT_ID'] + +request = json.load(sys.stdin) + +rdb = agent.redis_connect(privileged=True) +rdb.set(agent_id + '/ui_note', request['note']) diff --git a/core/imageroot/usr/local/agent/actions/set-note/validate-input.json b/core/imageroot/usr/local/agent/actions/set-note/validate-input.json new file mode 100644 index 000000000..225ccb2cc --- /dev/null +++ b/core/imageroot/usr/local/agent/actions/set-note/validate-input.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "set-note input", + "$id": "http://schema.nethserver.org/agent/set-note-input.json", + "description": "A short note to help identify or describe a module instance. The note is visible in Applications Center.", + "examples": [ + { + "note": "this module is for my personal use" + } + ], + "type": "object", + "required": [ + "note" + ], + "properties": { + "note": { + "type": "string", + "maxLength": 100 + } + } +} diff --git a/core/imageroot/usr/local/agent/pypkg/cluster/modules.py b/core/imageroot/usr/local/agent/pypkg/cluster/modules.py index 96d2fa889..123a292ff 100644 --- a/core/imageroot/usr/local/agent/pypkg/cluster/modules.py +++ b/core/imageroot/usr/local/agent/pypkg/cluster/modules.py @@ -387,6 +387,7 @@ def list_installed(rdb, skip_core_modules = False): 'version': mtag, 'module': msource.rsplit("/", 1)[1], 'ui_name': rdb.get(f'module/{module_id}/ui_name') or "", + 'ui_note': rdb.get(f'module/{module_id}/ui_note') or "", 'node': mnode_id, 'node_ui_name': hnode_names[mnode_id], 'logo': logos.get(module_id, ""), diff --git a/core/ui/package.json b/core/ui/package.json index 01e5b9b80..65fd6b089 100644 --- a/core/ui/package.json +++ b/core/ui/package.json @@ -16,7 +16,7 @@ "@carbon/icons-vue": "^10.37.0", "@carbon/themes": "^10.34.0", "@carbon/vue": "^2.40.0", - "@nethserver/ns8-ui-lib": "^1.5.0", + "@nethserver/ns8-ui-lib": "^1.6.0", "await-to-js": "^3.0.0", "axios": "^0.30.2", "carbon-components": "^10.41.0", diff --git a/core/ui/public/i18n/en/translation.json b/core/ui/public/i18n/en/translation.json index 509c8ff73..86c01638e 100644 --- a/core/ui/public/i18n/en/translation.json +++ b/core/ui/public/i18n/en/translation.json @@ -92,7 +92,9 @@ "not_available": "Not available", "release_notes": "Release notes", "plus_others": "+{num} other | +{num} others", - "choose_a_node": "Choose a node" + "choose_a_node": "Choose a node", + "type": "Type", + "any": "Any" }, "error": { "error": "Error", @@ -425,7 +427,8 @@ "get-metrics-id": "Get metrics ID", "restart-module": "Restart instance", "list-alerts": "List alerts", - "list-nodes": "List nodes" + "list-nodes": "List nodes", + "set-note": "Set note" }, "network": { "title": "Network" @@ -951,6 +954,8 @@ }, "software_center": { "title": "Software center", + "title_tooltip": "Discover, install and update applications in your cluster. To manage installed applications, go to {applications} page", + "go_to_applications_center": "Go to applications center", "search_placeholder": "Search apps", "software_updates": "Updates", "you_have_updates": "You have {numUpdates} app to update | You have {numUpdates} apps to update", @@ -1004,7 +1009,7 @@ "no_instance_installed": "No instance installed", "instance": "Instance", "instance_label": "App instance label", - "instance_label_tooltip": "You can assign the app instance an easy to remember name", + "instance_label_tooltip": "You can give the application a memorable label to help recall its name.", "edit_instance_label": "Edit instance label", "app_managed_in": "This app is managed in", "reload_software_repositories": "Reload repositories", @@ -1099,7 +1104,9 @@ "rootfull_certification_level_too_low": "Apps with administrative privileges must have a minimum certification level of {certification_min}/5 to be installed" }, "migrating_app_title": "This app instance is undergoing migration", - "migrating_app_description": "You'll be able to use {name} again once the migration from NethServer 7 is complete." + "migrating_app_description": "You'll be able to use {name} again once the migration from NethServer 7 is complete.", + "instance_label_too_long": "Application label must be 24 characters or less", + "instance_label_alphanum_only": "Application label can only contain alphanumeric characters and spaces" }, "system_logs": { "title": "System logs", @@ -1199,6 +1206,7 @@ "title": "Cluster status", "go_to_nodes": "Go to Nodes", "go_to_software_center": "Go to Software center", + "go_to_applications": "Go to Applications", "go_to_backup": "Go to Backup", "backups_failed_c": "{num} app backup failed | {num} app backups failed", "backup_disabled_c": "{num} backup disabled | {num} backups disabled", @@ -1676,5 +1684,40 @@ "available_with_subscription_title": "Activate subscription to enable this feature", "available_with_subscription": "Send a stream of security logs from applications and nodes of this cluster to Cloud Log Manager, where you can store, archive, and search them in a central place.", "cluster_id_notification": "Security logs from this cluster are available in the Cloud Log Manager UI under the host '{clusterId}'." + }, + "applications": { + "title": "Applications", + "title_tooltip": "This page displays all the applications installed on the cluster and lets you manage them easily. To install a new application, go to {software} page.", + "go_to_software_center": "Go to Software center", + "no_application": "No application installed", + "no_application_description": "You can find and install applications in the Software center.", + "name": "Name", + "version": "Version", + "type": "Type", + "node": "Node", + "status": "Status", + "update_available": "Update available", + "edit_label": "Edit label", + "restart": "Restart", + "open": "Open", + "clone": "Clone", + "move": "Move", + "uninstall": "Uninstall", + "instance_uninstallation": "Uninstall {instance}", + "uninstalling": "Uninstalling", + "restart_instance_name": "Restart {instance}", + "update": "Update", + "app_uninstallation": "Uninstall {app}", + "uninstall_app": "Uninstall {name}? App data will be deleted too. This action is NOT reversible", + "restarting": "Restarting", + "add_note": "Add note", + "edit_note": "Edit note", + "note": "Note", + "enter_note": "Enter note (max 100 characters)", + "note_too_long": "Note must be 100 characters or less", + "note_description": "A short note to help identify or describe this application.", + "note_alphanum_only": "Note can only contain alphanumeric characters and spaces", + "note_helper_text": "Only alphanumeric characters and spaces are allowed", + "filter_applications": "Filter applications" } } diff --git a/core/ui/src/components/applications-center/AddNoteModal.vue b/core/ui/src/components/applications-center/AddNoteModal.vue new file mode 100644 index 000000000..7ff76d204 --- /dev/null +++ b/core/ui/src/components/applications-center/AddNoteModal.vue @@ -0,0 +1,164 @@ + + + + diff --git a/core/ui/src/components/nodes/NodeCard.vue b/core/ui/src/components/nodes/NodeCard.vue index f011ee7b3..e5ae9eeff 100644 --- a/core/ui/src/components/nodes/NodeCard.vue +++ b/core/ui/src/components/nodes/NodeCard.vue @@ -98,10 +98,9 @@
{{ $t("nodes.applications") }}
- - {{ applications }} - - + + {{ applications }} +
@@ -245,8 +244,8 @@ export default { methods: { goToApplications() { this.$router.push({ - name: "applications", - params: { nodeId: this.nodeId }, + path: "/applications-center", + query: { selectedNodeId: this.nodeId }, }); }, }, diff --git a/core/ui/src/components/shell/SideMenuContent.vue b/core/ui/src/components/shell/SideMenuContent.vue index 48cdc51f5..1ea38a70a 100644 --- a/core/ui/src/components/shell/SideMenuContent.vue +++ b/core/ui/src/components/shell/SideMenuContent.vue @@ -36,9 +36,17 @@ @click="goTo('/software-center')" :active="isLinkActive('/software-center')" > - + {{ $t("software_center.title") }} + + + + {{ $t("applications.title") }} + + + + + + + + + + diff --git a/core/ui/src/components/software-center/SetInstanceLabelModal.vue b/core/ui/src/components/software-center/SetInstanceLabelModal.vue new file mode 100644 index 000000000..ed3dc2c40 --- /dev/null +++ b/core/ui/src/components/software-center/SetInstanceLabelModal.vue @@ -0,0 +1,147 @@ + + diff --git a/core/ui/src/router/index.js b/core/ui/src/router/index.js index a4972a374..6b825fc70 100644 --- a/core/ui/src/router/index.js +++ b/core/ui/src/router/index.js @@ -153,6 +153,14 @@ const routes = [ /* webpackChunkName: "software-center" */ "../views/SoftwareCenter.vue" ), }, + { + path: "/applications-center", + name: "ApplicationsCenter", + component: () => + import( + /* webpackChunkName: "applications-center" */ "../views/ApplicationsCenter.vue" + ), + }, { path: "/software-center/core-apps", name: "SoftwareCenterCoreApps", diff --git a/core/ui/src/views/ApplicationsCenter.vue b/core/ui/src/views/ApplicationsCenter.vue new file mode 100644 index 000000000..5bb0e705f --- /dev/null +++ b/core/ui/src/views/ApplicationsCenter.vue @@ -0,0 +1,892 @@ + + + + + + diff --git a/core/ui/src/views/ClusterStatus.vue b/core/ui/src/views/ClusterStatus.vue index e84940a00..2aa61609e 100644 --- a/core/ui/src/views/ClusterStatus.vue +++ b/core/ui/src/views/ClusterStatus.vue @@ -129,9 +129,9 @@ - {{ $t("cluster_status.go_to_software_center") }} + {{ $t("cluster_status.go_to_applications") }} diff --git a/core/ui/src/views/NodeDetail.vue b/core/ui/src/views/NodeDetail.vue index 24746beb0..ff33af636 100644 --- a/core/ui/src/views/NodeDetail.vue +++ b/core/ui/src/views/NodeDetail.vue @@ -119,9 +119,9 @@ {{ $t("node_detail.applications_count") }} - - {{ nodeStatus.app_count }} - + + {{ nodeStatus.app_count }} +