From 16d16c91800509fff6932fb16b03876e32ace4b6 Mon Sep 17 00:00:00 2001 From: cstns Date: Tue, 3 Dec 2024 17:51:15 +0200 Subject: [PATCH 01/28] add team bom page scaffolding --- frontend/src/pages/team/BOM/index.vue | 13 ++++++++++++ frontend/src/pages/team/routes.js | 6 ++++++ frontend/src/store/ux.js | 29 ++++++++++++++++++--------- 3 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 frontend/src/pages/team/BOM/index.vue diff --git a/frontend/src/pages/team/BOM/index.vue b/frontend/src/pages/team/BOM/index.vue new file mode 100644 index 000000000..1edb95f50 --- /dev/null +++ b/frontend/src/pages/team/BOM/index.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/frontend/src/pages/team/routes.js b/frontend/src/pages/team/routes.js index 9dfefbbde..b8690d702 100644 --- a/frontend/src/pages/team/routes.js +++ b/frontend/src/pages/team/routes.js @@ -2,6 +2,7 @@ import ensurePermission from '../../utils/ensurePermission.js' import TeamApplications from './Applications/index.vue' import TeamAuditLog from './AuditLog.vue' +import TeamBillOfMaterials from './BOM/index.vue' import TeamBilling from './Billing.vue' import TeamDevices from './Devices/index.vue' import TeamInstances from './Instances.vue' @@ -201,6 +202,11 @@ export default [ name: 'team-pipelines', path: 'pipelines', component: TeamPipelines + }, + { + name: 'team-bom', + path: 'bill-of-materials', + component: TeamBillOfMaterials } ] }, diff --git a/frontend/src/store/ux.js b/frontend/src/store/ux.js index fd19705e7..2de770979 100644 --- a/frontend/src/store/ux.js +++ b/frontend/src/store/ux.js @@ -2,7 +2,7 @@ import { BookOpenIcon, ChatIcon, ChipIcon, CogIcon, CollectionIcon, ColorSwatchIcon, CurrencyDollarIcon, DatabaseIcon, DesktopComputerIcon, LockClosedIcon, RssIcon, - TemplateIcon, UserGroupIcon, UsersIcon + TableIcon, TemplateIcon, UserGroupIcon, UsersIcon } from '@heroicons/vue/outline' import PipelinesIcon from '../components/icons/Pipelines.js' @@ -81,15 +81,6 @@ const getters = { title: 'Operations', hidden: !hasAMinimumTeamRoleOf(Roles.Viewer), entries: [ - { - label: 'Broker', - to: { name: 'team-unified-namespace', params: { team_slug: team.slug } }, - tag: 'team-unified-namespace', - icon: RssIcon, - disabled: noBilling, - featureUnavailable: !features.isMqttBrokerFeatureEnabled, - hidden: hasALowerOrEqualTeamRoleThan(Roles.Member) && features.isMqttBrokerFeatureEnabledForPlatform - }, { label: 'Pipelines', to: { name: 'team-pipelines', params: { team_slug: team.slug } }, @@ -98,6 +89,24 @@ const getters = { disabled: noBilling, featureUnavailable: !features.devOpsPipelinesFeatureEnabled, hidden: hasALowerOrEqualTeamRoleThan(Roles.Member) && features.devOpsPipelinesFeatureEnabled + }, + { + label: 'Bill Of Materials', + to: { name: 'team-bom', params: { team_slug: team.slug } }, + tag: 'team-bom', + icon: TableIcon, + disabled: noBilling, + featureUnavailable: !features.isBOMFeatureEnabled, + hidden: hasALowerOrEqualTeamRoleThan(Roles.Owner) && features.isBOMFeatureEnabled + }, + { + label: 'Broker', + to: { name: 'team-unified-namespace', params: { team_slug: team.slug } }, + tag: 'team-unified-namespace', + icon: RssIcon, + disabled: noBilling, + featureUnavailable: !features.isMqttBrokerFeatureEnabled, + hidden: hasALowerOrEqualTeamRoleThan(Roles.Member) && features.isMqttBrokerFeatureEnabledForPlatform } ] }, From f7f3c34fffa2a753c1eb7b9dcbb26678b085b87c Mon Sep 17 00:00:00 2001 From: cstns Date: Wed, 4 Dec 2024 14:06:39 +0200 Subject: [PATCH 02/28] Include instance/device status in the response payload --- forge/db/views/BOM.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/forge/db/views/BOM.js b/forge/db/views/BOM.js index 36bf642a0..8bd4f7e44 100644 --- a/forge/db/views/BOM.js +++ b/forge/db/views/BOM.js @@ -33,7 +33,8 @@ module.exports = { nullable: true }, ownerId: { type: 'string', nullable: true }, - dependencies: { type: 'array', items: { $ref: 'dependency' } } + dependencies: { type: 'array', items: { $ref: 'dependency' } }, + state: { type: 'string', nullable: true } } }) }, @@ -54,17 +55,17 @@ module.exports = { if (type !== null) { const dependenciesArray = Object.entries(dependencies || {}).map(([name, version]) => app.db.views.BOM.dependency(name, version?.wanted, version?.current)) if (type === 'device') { - const { hashid, name, ownerType } = model + const { hashid, name, ownerType, state } = model let ownerId = null if (ownerType === 'instance') { ownerId = model.ProjectId } else if (ownerType === 'application') { ownerId = model.Application ? model.Application.id : app.db.models.Application.encodeHashid(model.ApplicationId) } - return { id: hashid, name, type, ownerType, ownerId, dependencies: dependenciesArray } + return { id: hashid, name, type, ownerType, ownerId, dependencies: dependenciesArray, state } } else if (type === 'instance') { - const { id, name } = model - return { id, name, type, dependencies: dependenciesArray } + const { id, name, state } = model + return { id, name, type, dependencies: dependenciesArray, state } } } return null From 01aea6257b0c6a846a241cfc42e35ff799e7ff86 Mon Sep 17 00:00:00 2001 From: cstns Date: Wed, 4 Dec 2024 14:07:31 +0200 Subject: [PATCH 03/28] add a team bom api client --- frontend/src/api/team.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/api/team.js b/frontend/src/api/team.js index 6d4c08b34..4932eac1f 100644 --- a/frontend/src/api/team.js +++ b/frontend/src/api/team.js @@ -422,6 +422,16 @@ const bulkDeviceMove = async (teamId, devices, moveTo, id = undefined) => { return res.data } +/** + * Get a list of Dependencies / Bill of Materials + * @param teamId + * @returns {Promise>} + */ +const getDependencies = (teamId) => { + return client.get(`/api/v1/teams/${teamId}/bom`) + .then(res => res.data) +} + /** * Calls api routes in team.js * See [routes/api/team.js](../../../forge/routes/api/team.js) @@ -453,5 +463,6 @@ export default { updateTeamDeviceProvisioningToken, deleteTeamDeviceProvisioningToken, bulkDeviceDelete, - bulkDeviceMove + bulkDeviceMove, + getDependencies } From 96548619a0368edb1cc875187c8394f5857df989 Mon Sep 17 00:00:00 2001 From: cstns Date: Wed, 4 Dec 2024 14:09:01 +0200 Subject: [PATCH 04/28] use the response payload status instead of the parsed meta and extract common bom methods in a mixin --- frontend/src/mixins/BOM.js | 43 +++++++++++++++++++ .../components/DependencyItem.vue | 2 + .../Dependencies/components/InstancesItem.vue | 2 +- 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 frontend/src/mixins/BOM.js diff --git a/frontend/src/mixins/BOM.js b/frontend/src/mixins/BOM.js new file mode 100644 index 000000000..8b4badc09 --- /dev/null +++ b/frontend/src/mixins/BOM.js @@ -0,0 +1,43 @@ +export default { + computed: { + dependencies () { + return this.payload.children + .reduce((acc, currentInstance) => { + currentInstance.dependencies.forEach(dep => { + const searchTerm = this.searchTerm.trim() + const installedDependencyVersion = dep.version?.current ?? dep.version?.wanted ?? 'N/A' + const dependencyNameMatchesSearch = dep.name.toLowerCase().includes(searchTerm.toLowerCase()) + const dependencyVersionMatchesSearch = installedDependencyVersion.toLowerCase().includes(searchTerm.toLowerCase()) + const matchesInstanceName = currentInstance.name.toLowerCase().includes(searchTerm.toLowerCase()) + const includeDependency = () => { + if (!Object.prototype.hasOwnProperty.call(acc, dep.name)) { + acc[dep.name] = {} + } + if (!Object.prototype.hasOwnProperty.call(acc[dep.name], installedDependencyVersion)) { + acc[dep.name][installedDependencyVersion] = [] + } + acc[dep.name][installedDependencyVersion].push(currentInstance) + } + + switch (true) { + case !searchTerm.length: + includeDependency() + break + case matchesInstanceName: + includeDependency() + break + case dependencyVersionMatchesSearch || dependencyNameMatchesSearch: + includeDependency() + break + default: + break + } + }) + return acc + }, {}) + }, + hasInstances () { + return !(!this.payload || this.payload.children.length === 0) + } + } +} diff --git a/frontend/src/pages/application/Dependencies/components/DependencyItem.vue b/frontend/src/pages/application/Dependencies/components/DependencyItem.vue index 96d986036..4eac003bb 100644 --- a/frontend/src/pages/application/Dependencies/components/DependencyItem.vue +++ b/frontend/src/pages/application/Dependencies/components/DependencyItem.vue @@ -89,6 +89,8 @@ export default { From de81e041db63a0ffe75cc3a87fcdc00ccdf08180 Mon Sep 17 00:00:00 2001 From: cstns Date: Wed, 4 Dec 2024 14:14:20 +0200 Subject: [PATCH 06/28] move bom components in common components --- .../bill-of-materials}/DependencyItem.vue | 4 ++-- .../bill-of-materials}/InstancesItem.vue | 6 +++--- .../bill-of-materials}/VersionsList.vue | 2 +- .../src/pages/application/Dependencies/Dependencies.vue | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) rename frontend/src/{pages/application/Dependencies/components => components/bill-of-materials}/DependencyItem.vue (96%) rename frontend/src/{pages/application/Dependencies/components => components/bill-of-materials}/InstancesItem.vue (88%) rename frontend/src/{pages/application/Dependencies/components => components/bill-of-materials}/VersionsList.vue (97%) diff --git a/frontend/src/pages/application/Dependencies/components/DependencyItem.vue b/frontend/src/components/bill-of-materials/DependencyItem.vue similarity index 96% rename from frontend/src/pages/application/Dependencies/components/DependencyItem.vue rename to frontend/src/components/bill-of-materials/DependencyItem.vue index 4eac003bb..b8d967de7 100644 --- a/frontend/src/pages/application/Dependencies/components/DependencyItem.vue +++ b/frontend/src/components/bill-of-materials/DependencyItem.vue @@ -19,8 +19,8 @@ From 4e06a082b81ba85488520fc29ce3ac915f903230 Mon Sep 17 00:00:00 2001 From: cstns Date: Wed, 4 Dec 2024 14:29:28 +0200 Subject: [PATCH 08/28] display the app link on dependency items if the app is available --- .../bill-of-materials/InstancesItem.vue | 56 +++++++++++-------- frontend/src/pages/team/BOM/index.vue | 3 +- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/bill-of-materials/InstancesItem.vue b/frontend/src/components/bill-of-materials/InstancesItem.vue index 194a7c4c7..7fb769f2e 100644 --- a/frontend/src/components/bill-of-materials/InstancesItem.vue +++ b/frontend/src/components/bill-of-materials/InstancesItem.vue @@ -9,10 +9,15 @@
- + {{ instance.name }}
+
+ + {{ instance.app.name }} + +
@@ -27,7 +32,11 @@ import IconNodeRedSolid from '../icons/NodeRedSolid.js' export default { name: 'InstancesItem', - components: { StatusBadge, IconNodeRedSolid, IconDeviceSolid }, + components: { + StatusBadge, + IconNodeRedSolid, + IconDeviceSolid + }, props: { instance: { required: true, @@ -44,29 +53,32 @@ export default { diff --git a/frontend/src/pages/team/BOM/index.vue b/frontend/src/pages/team/BOM/index.vue index 69790d262..e8c885aa6 100644 --- a/frontend/src/pages/team/BOM/index.vue +++ b/frontend/src/pages/team/BOM/index.vue @@ -94,9 +94,10 @@ export default { payload () { const payload = { children: [] } this.intermediaryPayload.forEach(app => { + const { id, name } = app payload.children.push(...app.children.map(child => ({ ...child, - app + app: { id, name } }))) }) From 0e60dac5e2ba922c62193c491804e37daa23c47d Mon Sep 17 00:00:00 2001 From: cstns Date: Wed, 4 Dec 2024 14:37:07 +0200 Subject: [PATCH 09/28] truncate titles, versions and package names when resizing --- .../bill-of-materials/DependencyItem.vue | 10 +-- .../bill-of-materials/InstancesItem.vue | 2 +- .../bill-of-materials/VersionsList.vue | 74 ++++++++++--------- 3 files changed, 47 insertions(+), 39 deletions(-) diff --git a/frontend/src/components/bill-of-materials/DependencyItem.vue b/frontend/src/components/bill-of-materials/DependencyItem.vue index b8d967de7..85f597d23 100644 --- a/frontend/src/components/bill-of-materials/DependencyItem.vue +++ b/frontend/src/components/bill-of-materials/DependencyItem.vue @@ -1,12 +1,12 @@