From 7032926f64e6b65d03a7ef3b020777d9c38138b9 Mon Sep 17 00:00:00 2001 From: Alex-IS Date: Sun, 24 Sep 2023 20:20:25 +0200 Subject: [PATCH] fix(settings-plans-billing): do not poll the active plan, just refetch it when it changes --- .github/workflows/deploy-dev.yml | 1 + .github/workflows/deploy-eos-dev.yml | 2 +- src/components/dao/settings-plans-billing.vue | 80 +++++++++++++------ src/layouts/DhoSelector.vue | 29 +++---- src/utils/format-plan.js | 14 ++++ 5 files changed, 85 insertions(+), 41 deletions(-) create mode 100644 src/utils/format-plan.js diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index e6f49c031..90bed0daf 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -4,6 +4,7 @@ on: push: branches: - develop + - fix/billing-improvements jobs: build: diff --git a/.github/workflows/deploy-eos-dev.yml b/.github/workflows/deploy-eos-dev.yml index 22cdb8290..2401f2d4a 100644 --- a/.github/workflows/deploy-eos-dev.yml +++ b/.github/workflows/deploy-eos-dev.yml @@ -2,7 +2,7 @@ name: EOS - Build and Deploy to Development - TestNet on: push: - branches: [develop] + branches: [develop, fix/billing-improvements] jobs: build: diff --git a/src/components/dao/settings-plans-billing.vue b/src/components/dao/settings-plans-billing.vue index e9c674a4c..534216857 100644 --- a/src/components/dao/settings-plans-billing.vue +++ b/src/components/dao/settings-plans-billing.vue @@ -2,6 +2,7 @@ import { mapGetters } from 'vuex' import gql from 'graphql-tag' import { ORIGIN, PLAN_TYPE, PAYMENT_INTERVAL } from '~/const' +import formatPlan from '~/utils/format-plan' const STATES = Object.freeze({ WAITING: 'WAITING', @@ -22,6 +23,22 @@ const PLANS_QUERY = ` } ` +const ACTIVE_PLAN_QUERY = ` + activePlan(daoUrl: $daoUrl) { + subscriptionId + subscriptionItemId + subscriptionStatus + currency + currentPeriodEnd + currentPeriodStart + coreMembersCount + communityMembersCount + price + id: planId + name: planName + } + ` + export default { name: 'settings-plans-billing', components: { @@ -32,7 +49,7 @@ export default { props: { form: { type: Object, - default: () => {} + default: () => { } }, isAdmin: { @@ -43,14 +60,20 @@ export default { apollo: { _plans: { - query: gql`query PLANS { ${PLANS_QUERY} }`, + query: gql`query PLANS{ ${PLANS_QUERY} }`, update: data => data.getStripePrices, fetchPolicy: 'no-cache' + }, + activePlan: { + variables() { return { daoUrl: this.$route.params.dhoname } }, + query: gql`query activePlan($daoUrl: String!) { ${ACTIVE_PLAN_QUERY} }`, + update: data => formatPlan(data.activePlan), + fetchPolicy: 'no-cache' } }, - data () { + data() { return { STATES, state: STATES.WAITING, @@ -64,7 +87,7 @@ export default { }, methods: { - async _createCheckoutSession (id) { + async _createCheckoutSession(id) { this.state = STATES.CREATING_SESSION const res = await this.$apollo.mutate({ @@ -108,7 +131,7 @@ export default { } }, - async _updateSubscription (id) { + async _updateSubscription(id) { this.state = STATES.CREATING_SESSION const res = await this.$apollo.mutate({ @@ -139,16 +162,17 @@ export default { if (res) { this.state = STATES.WAITING + this.$apollo.queries.activePlan.refetch() } }, - onPlanDialogClose () { + onPlanDialogClose() { this.state = STATES.WAITING this.planType = PLAN_TYPE.SAAS this.paymentInterval = PAYMENT_INTERVAL.YEAR }, - switchPlanType () { + switchPlanType() { if (this.planType === PLAN_TYPE.SAAS) { this.planType = PLAN_TYPE.EAAS this.paymentInterval = null @@ -158,16 +182,20 @@ export default { } }, - formatMoney (amount) { return amount ? new Intl.NumberFormat().format(parseInt(amount), { style: 'currency' }) : 0 } + formatMoney(amount) { return amount ? new Intl.NumberFormat().format(parseInt(amount), { style: 'currency' }) : 0 } }, computed: { ...mapGetters('dao', ['daoSettings', 'selectedDao', 'selectedDaoPlan']), - isFreePlan () { return !this.selectedDaoPlan.id }, - isPlanModalOpen () { return [STATES.UPDATING_PLAIN, STATES.CREATING_SESSION].includes(this.state) }, + isFreePlan() { return !this.selectedDaoPlan.id }, + isPlanModalOpen() { return [STATES.UPDATING_PLAIN, STATES.CREATING_SESSION].includes(this.state) }, - plans () { + currentPlan() { + return this.activePlan.id ? this.activePlan : this.selectedDaoPlan + }, + + plans() { return this._plans .map(_ => ({ ..._, @@ -236,7 +264,7 @@ export default { div .text-xl.text-weight-600.text-primary {{ $t(`plans.${plan.name}`) }} p.q-pa-none.q-ma-none.text-3xl.text-primary.text-bold ${{ formatMoney(plan.amountUSD) }} - div(v-if="selectedDaoPlan.id === plan.id") + div(v-if="currentPlan.id === plan.id") q-chip(dense color="positive" text-color="white") span.text-uppercase.text-xxs.text-bold.q-px-xxs {{ $t(`statuses.active`) }} .hr.q-mt-md.q-mb-xs @@ -254,7 +282,7 @@ export default { nav.q-mt-xl.full-width.row.justify-end q-btn.q-px-xl.rounded-border.text-bold.q-ml-xs( - :disable="selectedDaoPlan.id === plan.id" + :disable="currentPlan.id === plan.id" :label="$t('configuration.settings-plans-billing.plan.modal.cta')" @click="isFreePlan ? _createCheckoutSession(plan.id) : _updateSubscription(plan.id)" color="secondary" @@ -272,35 +300,35 @@ export default { widget.q-mt-xl(bar shadow) header.row.justify-between div - .text-xl.text-weight-600.text-primary {{ $t(`plans.${selectedDaoPlan.name}`) }} - p.q-pa-none.q-ma-none.text-3xl.text-primary.text-bold ${{ formatMoney(selectedDaoPlan.amountUSD) }} + .text-xl.text-weight-600.text-primary {{ $t(`plans.${currentPlan.name}`) }} + p.q-pa-none.q-ma-none.text-3xl.text-primary.text-bold ${{ formatMoney(currentPlan.amountUSD) }} //- TODO: Return after beta //- span.q-ml-xxs.text-sm.text-weight-500 / {{ $('periods.month') }} div q-chip(dense color="positive" text-color="white") - span.text-uppercase.text-xxs.text-bold.q-px-xxs {{ $t(`statuses.${selectedDaoPlan.status}`) }} + span.text-uppercase.text-xxs.text-bold.q-px-xxs {{ $t(`statuses.${currentPlan.status}`) }} .hr.q-mt-md.q-mb-xs footer div.row.justify-between p.q-pa-none.q-ma-none.text-sm.text-h-gray.leading-loose Core Members - p.q-pa-none.q-ma-none.text-sm.text-h-gray.leading-loose {{ selectedDaoPlan.currentCoreMembersCount }} / {{ selectedDaoPlan.coreMembersCount }} + p.q-pa-none.q-ma-none.text-sm.text-h-gray.leading-loose {{ selectedDaoPlan.currentCoreMembersCount }} / {{ currentPlan.coreMembersCount }} //- TODO: Return after beta //- div.row.justify-between.q-mt-xs //- p.q-pa-none.q-ma-none.text-sm.text-h-gray.leading-loose Community Members //- p.q-pa-none.q-ma-none.text-sm.text-h-gray.leading-loose {{ selectedDaoPlan.communityMembersCount }} nav.q-mt-xl.full-width.row.justify-end - q-btn.q-px-xl.rounded-border.text-bold.q-ml-xs( - :disable="!isAdmin" - :label="$t('configuration.settings-plans-billing.plan.cta')" - @click="state = STATES.UPDATING_PLAIN" - color="secondary" - no-caps - rounded - unelevated - ) + q-btn.q-px-xl.rounded-border.text-bold.q-ml-xs( + :disable="!isAdmin" + :label="$t('configuration.settings-plans-billing.plan.cta')" + @click="state = STATES.UPDATING_PLAIN" + color="secondary" + no-caps + rounded + unelevated + ) .col-12.col-md-6(:class="{ 'q-pr-sm': $q.screen.gt.md, 'q-mt-md': $q.screen.lt.md }") widget(:title="$t('configuration.settings-plans-billing.history.title')" titleImage='/svg/briefcase.svg' bar).q-pa-none diff --git a/src/layouts/DhoSelector.vue b/src/layouts/DhoSelector.vue index c0f3983c1..66ef02e00 100644 --- a/src/layouts/DhoSelector.vue +++ b/src/layouts/DhoSelector.vue @@ -308,10 +308,10 @@ export default { dao: { query: gql`query activeDao($daoUrl: String!, $regexp: String!) { ${DAO_ACTIVE_QUERY} }`, update: data => data.queryDao, - skip () { return !this.dhoname || !this.daoRegexp }, - variables () { return { regexp: this.daoRegexp, daoUrl: this.dhoname } }, + skip() { return !this.dhoname || !this.daoRegexp }, + variables() { return { regexp: this.daoRegexp, daoUrl: this.dhoname } }, - result (res) { + result(res) { const data = res?.data if (!data?.queryDao?.length) { @@ -330,8 +330,8 @@ export default { this.$store.dispatch('accounts/checkMembership') }, - fetchPolicy: 'no-cache', - pollInterval: 1000 // TODO: Swap with subscribe once dgraph is ready + fetchPolicy: 'no-cache' + // pollInterval: 1000 // TODO: Swap with subscribe once dgraph is ready // subscribeToMore: { // document: gql`subscription activeDao($regexp: String!) { ${DAO_ACTIVE_QUERY} }`, // skip () { return !this.dhoname || !this.daoRegexp }, @@ -354,7 +354,7 @@ export default { dho: { query: require('~/query/main-dho.gql'), update: data => data.queryDho, - result (res) { + result(res) { this.$store.commit('dao/setDho', res.data.queryDho) }, fetchPolicy: 'no-cache' @@ -362,16 +362,16 @@ export default { }, - data () { + data() { return { daoQueryNumberOfRetires: 0 } }, computed: { - daoRegexp () { return '/^' + this.dhoname + '$/i' }, + daoRegexp() { return '/^' + this.dhoname + '$/i' }, - dho () { + dho() { if (this.dao && this.dao.length) { return { name: this.dao[0]?.details_daoName_n || '', @@ -389,10 +389,11 @@ export default { } }, - useCreateLayout () { return this.$q.screen.lt.md && this.$route.meta && this.$route.meta.layout && this.$route.meta.layout.mobile === 'create' }, - useLoginLayout () { return this.$route.name === 'login' }, - useMobileProposalLayout () { return this.$q.screen.lt.md && this.$route.meta && this.$route.meta.layout === 'proposal' }, - useMultiDHOLayout () { return this.$route.name !== 'login' } + useCreateLayout() { return this.$q.screen.lt.md && this.$route.meta && this.$route.meta.layout && this.$route.meta.layout.mobile === 'create' }, + useLoginLayout() { return this.$route.name === 'login' }, + useMobileProposalLayout() { return this.$q.screen.lt.md && this.$route.meta && this.$route.meta.layout === 'proposal' }, + useMultiDHOLayout() { return this.$route.name !== 'login' }, + refetch() { return this.$apollo.queries.dao.refetch } } } @@ -401,7 +402,7 @@ export default { .dho-selector create-layout(v-if="useCreateLayout") login-layout(v-if="useLoginLayout") - multi-dho-layout(v-if="useMultiDHOLayout" :dho="dho" :daoName="dhoname" :dhoTitle="dho?.title") + multi-dho-layout(v-if="useMultiDHOLayout" :dho="dho" :daoName="dhoname" :dhoTitle="dho?.title" :refetch="refetch") proposal-layout(v-if="useMobileProposalLayout && $q.platform.is.desktop") diff --git a/src/utils/format-plan.js b/src/utils/format-plan.js new file mode 100644 index 000000000..07203841b --- /dev/null +++ b/src/utils/format-plan.js @@ -0,0 +1,14 @@ +import { PLAN, PLAN_STATUS } from '~/const' + +export default (plan) => { + console.log('PLAN: ', plan) + + return { + ...plan, + name: (plan?.name || PLAN.FOUNDER).toLowerCase(), + status: plan?.status || PLAN_STATUS.ACTIVE, + amountUSD: plan?.price / 100 / 12, + coreMembersCount: plan?.coreMembersCount || 5, + communityMembersCount: plan?.communityMembersCount || 0 + } +}