Skip to content

Commit d9dda8b

Browse files
authored
Merge pull request #4298 from FlowFuse/4297-disable-nav-no-billing
Clearer communication of navigation restriction when billing is required
2 parents fa831ad + 84df462 commit d9dda8b

6 files changed

Lines changed: 45 additions & 37 deletions

File tree

frontend/src/components/SideNavigationTeamOptions.vue

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
<div v-for="route in navigation.general" :key="route.label">
77
<router-link
88
v-if="route.label"
9-
:class="{'router-link-active': atNestedRoute(route)}"
9+
:class="{'router-link-active': atNestedRoute(route), 'disabled': route.disabled}"
1010
:to="'/team/' + team.slug + route.to" :data-nav="route.tag"
1111
@click="$emit('option-selected')"
1212
>
13-
<nav-item :label="route.label" :icon="route.icon" :featureUnavailable="route.featureUnavailable" />
13+
<nav-item
14+
:label="route.label" :icon="route.icon"
15+
:featureUnavailable="route.featureUnavailable"
16+
/>
1417
</router-link>
1518
<div v-else class="ff-side-navigation-divider" />
1619
</div>
@@ -23,6 +26,7 @@
2326
<router-link
2427
v-for="route in navigation.admin" :key="route.label"
2528
:to="'/team/' + team.slug + route.to"
29+
:class="{'disabled': route.disabled}"
2630
:data-nav="route.tag"
2731
>
2832
<nav-item :icon="route.icon" :label="route.label" :featureUnavailable="route.featureUnavailable" />
@@ -37,7 +41,7 @@
3741

3842
<script>
3943
import { ChipIcon, CogIcon, CurrencyDollarIcon, DatabaseIcon, FolderIcon, TemplateIcon, UsersIcon } from '@heroicons/vue/solid'
40-
import { mapState } from 'vuex'
44+
import { mapGetters, mapState } from 'vuex'
4145
4246
import permissionsMixin from '../mixins/Permissions.js'
4347
@@ -65,6 +69,7 @@ export default {
6569
},
6670
computed: {
6771
...mapState('account', ['user', 'team', 'teamMembership', 'features', 'notifications']),
72+
...mapGetters('account', ['noBilling']),
6873
nested: function () {
6974
return (this.$slots['nested-menu'] && this.loaded) || this.closeNested
7075
},
@@ -74,40 +79,46 @@ export default {
7479
label: 'Applications',
7580
to: '/applications',
7681
tag: 'team-applications',
77-
icon: TemplateIcon
82+
icon: TemplateIcon,
83+
disabled: this.noBilling
7884
},
7985
{},
8086
{
8187
label: 'Instances',
8288
to: '/instances',
8389
tag: 'team-instances',
84-
icon: ProjectsIcon
90+
icon: ProjectsIcon,
91+
disabled: this.noBilling
8592
},
8693
{
8794
label: 'Devices',
8895
to: '/devices',
8996
tag: 'team-devices',
90-
icon: ChipIcon
97+
icon: ChipIcon,
98+
disabled: this.noBilling
9199
},
92100
{},
93101
{
94102
label: 'Library',
95103
to: '/library',
96104
tag: 'shared-library',
97105
icon: FolderIcon,
106+
disabled: this.noBilling,
98107
featureUnavailable: !this.features?.['shared-library'] || this.team?.type.properties.features?.['shared-library'] === false
99108
},
100109
{
101110
label: 'Members',
102111
to: '/members',
103112
tag: 'team-members',
104-
icon: UsersIcon
113+
icon: UsersIcon,
114+
disabled: this.noBilling
105115
}],
106116
admin: [{
107117
label: 'Audit Log',
108118
to: '/audit-log',
109119
tag: 'team-audit',
110-
icon: DatabaseIcon
120+
icon: DatabaseIcon,
121+
disabled: this.noBilling
111122
},
112123
{
113124
label: 'Team Settings',

frontend/src/pages/team/Billing.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
You have <span class="font-bold" v-text="trialEndsIn" /> left of your trial.
6363
</p>
6464
<p>
65-
During the trial you can make full use of the features available to your team. To keep things running you will need to setup your billing details.
65+
You must add billing details in order to continue using FlowFuse.
6666
</p>
6767
</template>
6868
<template v-else>

frontend/src/pages/team/index.vue

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
<script>
3636
import { useRoute } from 'vue-router'
37-
import { mapState } from 'vuex'
37+
import { mapGetters, mapState } from 'vuex'
3838
3939
import { Roles } from '../../../../forge/lib/roles.js'
4040
@@ -67,6 +67,7 @@ export default {
6767
},
6868
computed: {
6969
...mapState('account', ['user', 'team', 'teamMembership', 'pendingTeamChange', 'features']),
70+
...mapGetters('account', ['noBilling']),
7071
isVisitingAdmin: function () {
7172
return (this.teamMembership.role === Roles.Admin)
7273
},
@@ -104,12 +105,7 @@ export default {
104105
},
105106
checkBilling: async function () {
106107
// Team Billing
107-
if (!this.user.admin &&
108-
this.features.billing &&
109-
(!this.team.billing?.unmanaged) &&
110-
(!this.team.billing?.trial || this.team.billing?.trialEnded) &&
111-
!this.team.billing?.active
112-
) {
108+
if (this.noBilling) {
113109
this.$router.push({
114110
path: `/team/${this.team.slug}/billing`
115111
})

frontend/src/store/account.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ const getters = {
7373
offline (state) {
7474
return state.offline
7575
},
76+
noBilling (state) {
77+
return !state.user.admin &&
78+
state.features.billing &&
79+
(!state.team.billing?.unmanaged) &&
80+
(!state.team.billing?.trial || state.team.billing?.trialEnded) &&
81+
!state.team.billing?.active
82+
},
7683
isAdminUser: (state) => !!state.user.admin,
7784
defaultUserTeam: (state, getters) => {
7885
const defaultTeamId = state.user.defaultTeam || getters.teams[0]?.id

frontend/src/stylesheets/layouts.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,10 @@ $nav_height: 60px;
675675
background-color: $ff-grey-700;
676676
border-bottom: 1px solid $ff-grey-600;
677677
}
678+
.disabled {
679+
pointer-events: none;
680+
opacity: 0.5;
681+
}
678682
.ff-notification-pill {
679683
padding: 2px 12px;
680684
background-color: $ff-red-600;

test/e2e/frontend/cypress/tests/team/billing.js

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe('FlowForge - Team Billing', () => {
5353
})
5454

5555
describe('Cancelled subscriptions', () => {
56-
it('redirects regular users to the billing page', () => {
56+
beforeEach(() => {
5757
cy.login('bob', 'bbPassword')
5858

5959
cy.intercept('GET', '/api/v1/teams/*', (req) => req.reply(res => {
@@ -76,26 +76,16 @@ describe('FlowForge - Team Billing', () => {
7676
cy.visit('/')
7777

7878
cy.wait('@getTeams')
79-
80-
cy.url().should('include', '/team/ateam/billing')
81-
82-
cy.get('[data-nav="team-applications"').click()
83-
cy.url().should('include', '/team/ateam/billing')
84-
cy.get('[data-nav="team-instances"').click()
85-
cy.url().should('include', '/team/ateam/billing')
86-
cy.get('[data-nav="team-devices"').click()
87-
cy.url().should('include', '/team/ateam/billing')
88-
cy.get('[data-nav="shared-library"').click()
89-
cy.url().should('include', '/team/ateam/billing')
90-
cy.get('[data-nav="team-members"').click()
91-
cy.url().should('include', '/team/ateam/billing')
92-
cy.get('[data-nav="team-audit"').click()
93-
cy.url().should('include', '/team/ateam/billing')
94-
cy.get('[data-nav="team-billing"').click()
95-
cy.url().should('include', '/team/ateam/billing')
96-
97-
cy.get('[data-nav="team-settings"').click()
98-
cy.url().should('include', '/team/ateam/settings/general')
79+
})
80+
it('cannot interact with navigation options other than Team Settings & Billing', () => {
81+
cy.get('[data-nav="team-applications"').should('have.class', 'disabled')
82+
cy.get('[data-nav="team-instances"').should('have.class', 'disabled')
83+
cy.get('[data-nav="team-devices"').should('have.class', 'disabled')
84+
cy.get('[data-nav="shared-library"').should('have.class', 'disabled')
85+
cy.get('[data-nav="team-members"').should('have.class', 'disabled')
86+
cy.get('[data-nav="team-audit"').should('have.class', 'disabled')
87+
cy.get('[data-nav="team-billing"').should('not.have.class', 'disabled')
88+
cy.get('[data-nav="team-settings"').should('not.have.class', 'disabled')
9989
})
10090

10191
it('allows admins to navigate the team', () => {

0 commit comments

Comments
 (0)