Skip to content

Commit

Permalink
Merge pull request #4298 from FlowFuse/4297-disable-nav-no-billing
Browse files Browse the repository at this point in the history
Clearer communication of navigation restriction when billing is required
  • Loading branch information
knolleary authored Aug 1, 2024
2 parents fa831ad + 84df462 commit d9dda8b
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 37 deletions.
27 changes: 19 additions & 8 deletions frontend/src/components/SideNavigationTeamOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
<div v-for="route in navigation.general" :key="route.label">
<router-link
v-if="route.label"
:class="{'router-link-active': atNestedRoute(route)}"
:class="{'router-link-active': atNestedRoute(route), 'disabled': route.disabled}"
:to="'/team/' + team.slug + route.to" :data-nav="route.tag"
@click="$emit('option-selected')"
>
<nav-item :label="route.label" :icon="route.icon" :featureUnavailable="route.featureUnavailable" />
<nav-item
:label="route.label" :icon="route.icon"
:featureUnavailable="route.featureUnavailable"
/>
</router-link>
<div v-else class="ff-side-navigation-divider" />
</div>
Expand All @@ -23,6 +26,7 @@
<router-link
v-for="route in navigation.admin" :key="route.label"
:to="'/team/' + team.slug + route.to"
:class="{'disabled': route.disabled}"
:data-nav="route.tag"
>
<nav-item :icon="route.icon" :label="route.label" :featureUnavailable="route.featureUnavailable" />
Expand All @@ -37,7 +41,7 @@

<script>
import { ChipIcon, CogIcon, CurrencyDollarIcon, DatabaseIcon, FolderIcon, TemplateIcon, UsersIcon } from '@heroicons/vue/solid'
import { mapState } from 'vuex'
import { mapGetters, mapState } from 'vuex'
import permissionsMixin from '../mixins/Permissions.js'
Expand Down Expand Up @@ -65,6 +69,7 @@ export default {
},
computed: {
...mapState('account', ['user', 'team', 'teamMembership', 'features', 'notifications']),
...mapGetters('account', ['noBilling']),
nested: function () {
return (this.$slots['nested-menu'] && this.loaded) || this.closeNested
},
Expand All @@ -74,40 +79,46 @@ export default {
label: 'Applications',
to: '/applications',
tag: 'team-applications',
icon: TemplateIcon
icon: TemplateIcon,
disabled: this.noBilling
},
{},
{
label: 'Instances',
to: '/instances',
tag: 'team-instances',
icon: ProjectsIcon
icon: ProjectsIcon,
disabled: this.noBilling
},
{
label: 'Devices',
to: '/devices',
tag: 'team-devices',
icon: ChipIcon
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',
tag: 'team-members',
icon: UsersIcon
icon: UsersIcon,
disabled: this.noBilling
}],
admin: [{
label: 'Audit Log',
to: '/audit-log',
tag: 'team-audit',
icon: DatabaseIcon
icon: DatabaseIcon,
disabled: this.noBilling
},
{
label: 'Team Settings',
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/team/Billing.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
You have <span class="font-bold" v-text="trialEndsIn" /> left of your trial.
</p>
<p>
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.
You must add billing details in order to continue using FlowFuse.
</p>
</template>
<template v-else>
Expand Down
10 changes: 3 additions & 7 deletions frontend/src/pages/team/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

<script>
import { useRoute } from 'vue-router'
import { mapState } from 'vuex'
import { mapGetters, mapState } from 'vuex'
import { Roles } from '../../../../forge/lib/roles.js'
Expand Down Expand Up @@ -67,6 +67,7 @@ export default {
},
computed: {
...mapState('account', ['user', 'team', 'teamMembership', 'pendingTeamChange', 'features']),
...mapGetters('account', ['noBilling']),
isVisitingAdmin: function () {
return (this.teamMembership.role === Roles.Admin)
},
Expand Down Expand Up @@ -104,12 +105,7 @@ export default {
},
checkBilling: async function () {
// Team Billing
if (!this.user.admin &&
this.features.billing &&
(!this.team.billing?.unmanaged) &&
(!this.team.billing?.trial || this.team.billing?.trialEnded) &&
!this.team.billing?.active
) {
if (this.noBilling) {
this.$router.push({
path: `/team/${this.team.slug}/billing`
})
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/store/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ const getters = {
offline (state) {
return state.offline
},
noBilling (state) {
return !state.user.admin &&
state.features.billing &&
(!state.team.billing?.unmanaged) &&
(!state.team.billing?.trial || state.team.billing?.trialEnded) &&
!state.team.billing?.active
},
isAdminUser: (state) => !!state.user.admin,
defaultUserTeam: (state, getters) => {
const defaultTeamId = state.user.defaultTeam || getters.teams[0]?.id
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/stylesheets/layouts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,10 @@ $nav_height: 60px;
background-color: $ff-grey-700;
border-bottom: 1px solid $ff-grey-600;
}
.disabled {
pointer-events: none;
opacity: 0.5;
}
.ff-notification-pill {
padding: 2px 12px;
background-color: $ff-red-600;
Expand Down
32 changes: 11 additions & 21 deletions test/e2e/frontend/cypress/tests/team/billing.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('FlowForge - Team Billing', () => {
})

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

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

cy.wait('@getTeams')

cy.url().should('include', '/team/ateam/billing')

cy.get('[data-nav="team-applications"').click()
cy.url().should('include', '/team/ateam/billing')
cy.get('[data-nav="team-instances"').click()
cy.url().should('include', '/team/ateam/billing')
cy.get('[data-nav="team-devices"').click()
cy.url().should('include', '/team/ateam/billing')
cy.get('[data-nav="shared-library"').click()
cy.url().should('include', '/team/ateam/billing')
cy.get('[data-nav="team-members"').click()
cy.url().should('include', '/team/ateam/billing')
cy.get('[data-nav="team-audit"').click()
cy.url().should('include', '/team/ateam/billing')
cy.get('[data-nav="team-billing"').click()
cy.url().should('include', '/team/ateam/billing')

cy.get('[data-nav="team-settings"').click()
cy.url().should('include', '/team/ateam/settings/general')
})
it('cannot interact with navigation options other than Team Settings & Billing', () => {
cy.get('[data-nav="team-applications"').should('have.class', 'disabled')
cy.get('[data-nav="team-instances"').should('have.class', 'disabled')
cy.get('[data-nav="team-devices"').should('have.class', 'disabled')
cy.get('[data-nav="shared-library"').should('have.class', 'disabled')
cy.get('[data-nav="team-members"').should('have.class', 'disabled')
cy.get('[data-nav="team-audit"').should('have.class', 'disabled')
cy.get('[data-nav="team-billing"').should('not.have.class', 'disabled')
cy.get('[data-nav="team-settings"').should('not.have.class', 'disabled')
})

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

0 comments on commit d9dda8b

Please sign in to comment.