Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Team broker client UI #4646

Merged
merged 59 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
6b0b093
First pass
hardillb Oct 15, 2024
a640096
Mode api
hardillb Oct 15, 2024
63d9ecc
Add make delete work
hardillb Oct 15, 2024
996ea46
First pass
hardillb Oct 15, 2024
f9ea746
Mode api
hardillb Oct 15, 2024
cb7f172
Add make delete work
hardillb Oct 15, 2024
298d22a
Use new API
hardillb Oct 15, 2024
cbb199c
Merge branch 'emqx-backend-update' into team-broker-client-ui
hardillb Oct 15, 2024
c0e19f4
Merge branch 'team-broker-client-ui' of https://github.com/FlowFuse/f…
hardillb Oct 15, 2024
f902011
Basic add client
hardillb Oct 15, 2024
d659b92
Merge branch 'emqx-backend-update' into team-broker-client-ui
hardillb Oct 16, 2024
d31b89e
Add modifyClient to api
hardillb Oct 16, 2024
acfbdd4
Merge branch 'emqx-backend-update' into team-broker-client-ui
hardillb Oct 16, 2024
d71b647
Merge branch 'emqx-backend-update' into team-broker-client-ui
hardillb Oct 16, 2024
1c208c0
Merge branch 'emqx-backend-update' into team-broker-client-ui
hardillb Oct 16, 2024
88c3cc1
Merge branch 'emqx-backend-update' into team-broker-client-ui
hardillb Oct 21, 2024
91e226d
create mqtt client ux
cstns Oct 22, 2024
2c07cec
initialize the first acl rule with # wildcard
cstns Oct 22, 2024
8ad6629
Merge branch 'main' into team-broker-client-ui
hardillb Oct 22, 2024
60ed119
implement broker ui based on provided mockup
cstns Oct 22, 2024
8891724
qf padding
cstns Oct 22, 2024
8e36185
hide broker menu entry and broker clients create/update/delete button…
cstns Oct 23, 2024
fba6167
add broker navigation guard
cstns Oct 23, 2024
b333de2
add broker feature checks and mark it as not available for team tier …
cstns Oct 23, 2024
9625ab7
Add broker platform banners and fix team library banner padding
cstns Oct 23, 2024
0b38029
code cleanup
cstns Oct 23, 2024
6d30b9b
append the team id to broker client usernames
cstns Oct 23, 2024
356cc55
adding delete broker client and styling
cstns Oct 23, 2024
4044e84
adding the ability to update broker clients from the ui
cstns Oct 23, 2024
8e21f00
update mqtt pictograms
cstns Oct 23, 2024
fdec3e8
Update frontend/src/api/broker.js
cstns Oct 23, 2024
f71c026
Update forge/ee/routes/teamBroker/index.js
cstns Oct 23, 2024
5a8d596
disable client username for editing and fix lingering default acl
cstns Oct 23, 2024
11ec768
Update frontend/src/pages/team/Broker/index.vue
cstns Oct 23, 2024
b3f91cf
Merge remote-tracking branch 'origin/team-broker-client-ui' into team…
cstns Oct 23, 2024
c3c2536
Add pattern validator
hardillb Oct 23, 2024
15b3e67
Merge branch 'team-broker-client-ui' of https://github.com/FlowFuse/f…
hardillb Oct 23, 2024
d7a22d9
qf broken filter for the side menu
cstns Oct 23, 2024
c3be4a7
Fix pattern validator
hardillb Oct 23, 2024
d162515
Merge branch 'team-broker-client-ui' of https://github.com/FlowFuse/f…
hardillb Oct 23, 2024
0915c49
Fix lint
hardillb Oct 23, 2024
0e288a0
ignore eslint warning
cstns Oct 23, 2024
2a5f243
ignore eslint warning in e2e tests and fix routes import order
cstns Oct 23, 2024
1c7f1b0
Merge remote-tracking branch 'origin/main' into team-broker-client-ui
cstns Oct 23, 2024
1841cb2
move the broker menu entry under the instances/devices menu divider
cstns Oct 23, 2024
8e16442
prevent feature banners from being displayed at the same time on the …
cstns Oct 23, 2024
c72e1d1
add os e2e tests
cstns Oct 23, 2024
a08931d
Add empty state image
hardillb Oct 23, 2024
888012c
Merge branch 'team-broker-client-ui' of https://github.com/FlowFuse/f…
hardillb Oct 23, 2024
3d53aa7
Use correct permission for create button
hardillb Oct 23, 2024
34bf8d0
refine the broker's empty state message
cstns Oct 23, 2024
a5ac3dc
Merge remote-tracking branch 'origin/team-broker-client-ui' into team…
cstns Oct 23, 2024
fdc16e9
refine the broker's empty state message
cstns Oct 23, 2024
f575c64
Add correct empty state image
joepavitt Oct 23, 2024
1fea024
qf eslint disable
cstns Oct 23, 2024
621e717
Merge remote-tracking branch 'origin/team-broker-client-ui' into team…
cstns Oct 23, 2024
e58196c
add ee e2e tests for the broker page
cstns Oct 23, 2024
420db8d
qf linter
cstns Oct 23, 2024
9d7eb76
fix hardcoded project id
cstns Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions frontend/src/api/broker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import paginateUrl from '../utils/paginateUrl.js'

import client from './client.js'

const getClients = (teamId, params, cursor, limit) => {
const url = paginateUrl(`/api/v1/teams/${teamId}/broker/clients`, cursor, limit)
return client.get(url, { params }).then(res => res.data)
}

const getClient = (teamId, username) => {
return client.get(`/api/v1/teams/${teamId}/broker/client/${username}`).then(res => res.data)
}

const createClient = (teamId, username, password, acls) => {
return client.post(`/api/v1/teams/${teamId}/broker/client`, {
username,
password,
acls
}).then(res => res.data)
}

const deleteClient = (teamId, username) => {
return client.delete(`/api/v1/teams/${teamId}/broker/client/${username}`).then(res => res.data)
}

const updateClient = (teamId, username, { acls, password }) => {
return client.put(`/api/v1/teams/${teamId}/broker/client/${username}`, {
acls,
password
}).then(res => res.data)
}

export default {
getClients,
getClient,
createClient,
updateClient,
deleteClient
}
2 changes: 1 addition & 1 deletion frontend/src/components/Accordion.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="ff-accordion" :class="{'open': isOpen}">
<div class="ff-accordion" :class="{'open': isOpen}" data-el="accordion">
<button class="ff-accordion--button" :disabled="disabled" @click="toggle()">
<slot name="label">
<label>{{ label }}</label>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/EmptyState.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div data-el="empty-state">
<FeatureUnavailable v-if="featureUnavailable" :message="featureUnavailableMessage" />
<FeatureUnavailableToTeam v-if="featureUnavailableToTeam" />
<FeatureUnavailableToTeam v-else-if="featureUnavailableToTeam" />
<div
class="ff-empty-state" :class="{'ff-empty-state-feature-unavailable': featureUnavailable}"
>
Expand Down
147 changes: 86 additions & 61 deletions frontend/src/components/SideNavigationTeamOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@
</template>

<script>
import { ChipIcon, CogIcon, CurrencyDollarIcon, DatabaseIcon, FolderIcon, TemplateIcon, UsersIcon } from '@heroicons/vue/solid'
import { ChipIcon, CogIcon, CurrencyDollarIcon, DatabaseIcon, FolderIcon, RssIcon, TemplateIcon, UsersIcon } from '@heroicons/vue/solid'
import { mapGetters, mapState } from 'vuex'

import featuresMixin from '../mixins/Features.js'
import permissionsMixin from '../mixins/Permissions.js'
import { Roles } from '../utils/roles.js'

import NavItem from './NavItem.vue'
import ProjectsIcon from './icons/Projects.js'
Expand All @@ -53,7 +55,7 @@ export default {
components: {
NavItem
},
mixins: [permissionsMixin],
mixins: [permissionsMixin, featuresMixin],
props: {
mobileMenuOpen: {
type: Boolean,
Expand All @@ -75,68 +77,91 @@ export default {
},
navigation () {
const result = {
general: [{
label: 'Applications',
to: '/applications',
tag: 'team-applications',
icon: TemplateIcon,
disabled: this.noBilling
},
{},
{
label: 'Instances',
to: '/instances',
tag: 'team-instances',
icon: ProjectsIcon,
disabled: this.noBilling
},
{
label: 'Devices',
to: '/devices',
tag: 'team-devices',
icon: ChipIcon,
disabled: this.noBilling
},
{},
{
label: 'Library',
to: '/library',
tag: 'shared-library',
icon: FolderIcon,
disabled: this.noBilling,
featureUnavailable: !this.features?.['shared-library'] || this.team?.type.properties.features?.['shared-library'] === false
},
{
label: 'Members',
to: '/members/general',
tag: 'team-members',
icon: UsersIcon,
disabled: this.noBilling
}],
admin: [{
label: 'Audit Log',
to: '/audit-log',
tag: 'team-audit',
icon: DatabaseIcon,
disabled: this.noBilling
},
{
label: 'Team Settings',
to: '/settings',
tag: 'team-settings',
icon: CogIcon
}]
general: [
{
label: 'Applications',
to: '/applications',
tag: 'team-applications',
icon: TemplateIcon,
disabled: this.noBilling
},
{},
{
label: 'Instances',
to: '/instances',
tag: 'team-instances',
icon: ProjectsIcon,
disabled: this.noBilling
},
{
label: 'Devices',
to: '/devices',
tag: 'team-devices',
icon: ChipIcon,
disabled: this.noBilling
},
{},
{
label: 'Broker',
to: '/broker',
tag: 'team-broker',
icon: RssIcon,
disabled: this.noBilling,
featureUnavailable: !this.isMqttBrokerFeatureEnabled,
hidden: this.hasALowerOrEqualTeamRoleThan(Roles.Member)
},
{
label: 'Library',
to: '/library',
tag: 'shared-library',
icon: FolderIcon,
disabled: this.noBilling,
featureUnavailable: !this.features?.['shared-library'] || this.team?.type.properties.features?.['shared-library'] === false
},
{
label: 'Members',
to: '/members/general',
tag: 'team-members',
icon: UsersIcon,
disabled: this.noBilling
}
],
admin: [
{
label: 'Audit Log',
to: '/audit-log',
tag: 'team-audit',
icon: DatabaseIcon,
disabled: this.noBilling
},
{
label: 'Billing',
to: '/billing',
tag: 'team-billing',
icon: CurrencyDollarIcon,
hidden: !!this.features?.billing
},
{
label: 'Team Settings',
to: '/settings',
tag: 'team-settings',
icon: CogIcon
}
]
}
if (this.features?.billing) {
// insert billing in second slot of admin
result.admin.splice(1, 0, {
label: 'Billing',
to: '/billing',
tag: 'team-billing',
icon: CurrencyDollarIcon

return {
general: result.general.filter(entry => {
if (Object.prototype.hasOwnProperty.call(entry, 'hidden')) {
return entry.hidden
} else return true
}),
admin: result.admin.filter(entry => {
if (Object.prototype.hasOwnProperty.call(entry, 'hidden')) {
return entry.hidden
} else return true
})
}
return result
}
},
mounted () {
Expand Down
Binary file added frontend/src/images/empty-states/mqtt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions frontend/src/mixins/Features.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ export default {
},
isTimelineFeatureEnabled () {
return this.isTimelineFeatureEnabledForPlatform && this.isTimelineFeatureEnabledForTeam
},
isMqttBrokerFeatureEnabledForPlatform () {
return !!this.features.teamBroker
},
isMqttBrokerFeatureEnabledForTeam () {
return !!this.team?.type?.properties?.features?.teamBroker
},
isMqttBrokerFeatureEnabled () {
return this.isMqttBrokerFeatureEnabledForPlatform && this.isMqttBrokerFeatureEnabledForTeam
}
}
}
16 changes: 16 additions & 0 deletions frontend/src/mixins/Permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ export default {
}

return this.teamMembership?.role >= role
},

/**
* Check if the user has a lower role than given role.
* @param {Role} role - The role to check against.
* @returns {boolean} True if the user has a lower role than the given one, otherwise false.
* @example
* // Check if the user has role lower than 'Member' role
* const isMemberOrHigher = hasALowerTeamRoleThan(Roles.Member)
*/
hasALowerOrEqualTeamRoleThan (role) {
if (this.isVisitingAdmin) {
return true
}

return role <= this.teamMembership?.role
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import daysSince from '../../../../../utils/daysSince.js'

import TimelineGraph from './TimelineGraph.vue'

// eslint-disable-next-line vue/one-component-per-file
export default {
name: 'TimelineEvent',
components: { TimelineGraph },
Expand Down Expand Up @@ -123,6 +124,7 @@ export default {
// we can only differentiate between a plain snapshot import and a devops deployment history event
// by its data payload (i.e. if the event has a data.sourceProject attr, we know it's from a devops pipeline)

// eslint-disable-next-line vue/one-component-per-file
return defineComponent({
emits: ['preview-snapshot', 'preview-instance'],
methods: {
Expand All @@ -140,6 +142,7 @@ export default {
})
}

// eslint-disable-next-line vue/one-component-per-file
return defineComponent({
emits: ['preview-snapshot', 'preview-instance'],
methods: {
Expand All @@ -157,6 +160,7 @@ export default {
})

case this.event.event === 'project.snapshot.rolled-back':
// eslint-disable-next-line vue/one-component-per-file
return defineComponent({
emits: ['preview-snapshot'],
methods: {
Expand All @@ -169,6 +173,7 @@ export default {
</span>`
})
case this.event.event === 'project.snapshot.created':
// eslint-disable-next-line vue/one-component-per-file
return defineComponent({
emits: ['preview-snapshot'],
methods: {
Expand All @@ -182,18 +187,22 @@ export default {
</span>`
})
case this.event.event === 'flows.set':
// eslint-disable-next-line vue/one-component-per-file
return defineComponent({
template: '<span>Flows Deployed From Editor</span>'
})
case this.event.event === 'project.created':
// eslint-disable-next-line vue/one-component-per-file
return defineComponent({
template: '<span>Instance Created</span>'
})
case this.event.event === 'project.settings.updated':
// eslint-disable-next-line vue/one-component-per-file
return defineComponent({
template: '<span>Instance Settings Updated</span>'
})
default:
// eslint-disable-next-line vue/one-component-per-file
return defineComponent({
template: `<span>${this.event.event}</span>`
})
Expand Down
Loading
Loading