Merge commit

This commit is contained in:
Dean 2022-09-13 11:52:31 +01:00
parent 1a6d50008e
commit 60feccaac0
19 changed files with 707 additions and 81 deletions

View File

@ -4,22 +4,30 @@
import { banner } from "../Stores/banner"
import Banner from "./Banner.svelte"
import { fly } from "svelte/transition"
import TooltipWrapper from "../Tooltip/TooltipWrapper.svelte"
</script>
<Portal target=".banner-container">
<div class="banner">
{#if $banner.message}
{#each $banner.messages as message}
<div transition:fly={{ y: -30 }}>
<Banner
type={$banner.type}
extraButtonText={$banner.extraButtonText}
extraButtonAction={$banner.extraButtonAction}
on:change={$banner.onChange}
type={message.type}
extraButtonText={message.extraButtonText}
extraButtonAction={message.extraButtonAction}
on:change={() => {
message.onChange()
}}
showCloseButton={typeof message.showCloseButton === "boolean"
? message.showCloseButton
: true}
>
{$banner.message}
<TooltipWrapper tooltip={"test"}>
{message.message}
</TooltipWrapper>
</Banner>
</div>
{/if}
{/each}
</div>
</Portal>

View File

@ -1,7 +1,9 @@
import { writable } from "svelte/store"
export function createBannerStore() {
const DEFAULT_CONFIG = {}
const DEFAULT_CONFIG = {
messages: [],
}
const banner = writable(DEFAULT_CONFIG)
@ -28,9 +30,23 @@ export function createBannerStore() {
await show(config)
}
const queue = async entries => {
banner.update(store => {
const sorted = [...store.messages, ...entries].sort(
(a, b) => a.priority > b.priority
)
return {
...store,
messages: sorted,
}
})
}
return {
subscribe: banner.subscribe,
showStatus,
show,
queue,
}
}

View File

@ -54,7 +54,6 @@
transform: scale(0.75);
}
.icon-small {
margin-top: -2px;
margin-bottom: -5px;
margin-bottom: -2px;
}
</style>

View File

@ -34,6 +34,7 @@ export { default as Layout } from "./Layout/Layout.svelte"
export { default as Page } from "./Layout/Page.svelte"
export { default as Link } from "./Link/Link.svelte"
export { default as Tooltip } from "./Tooltip/Tooltip.svelte"
export { default as TooltipWrapper } from "./Tooltip/TooltipWrapper.svelte"
export { default as Menu } from "./Menu/Menu.svelte"
export { default as MenuSection } from "./Menu/Section.svelte"
export { default as MenuSeparator } from "./Menu/Separator.svelte"

View File

@ -4,6 +4,7 @@
import { NotificationDisplay, BannerDisplay } from "@budibase/bbui"
import { parse, stringify } from "qs"
import HelpIcon from "components/common/HelpIcon.svelte"
import LicensingOverlays from "components/portal/licensing/LicensingOverlays.svelte"
const queryHandler = { parse, stringify }
</script>
@ -12,6 +13,9 @@
<BannerDisplay />
<NotificationDisplay />
<LicensingOverlays />
<Router {routes} config={{ queryHandler }} />
<div class="modal-container" />
<HelpIcon />

View File

@ -1,5 +1,6 @@
import { getFrontendStore } from "./store/frontend"
import { getAutomationStore } from "./store/automation"
import { getTemporalStore } from "./store/temporal"
import { getThemeStore } from "./store/theme"
import { derived } from "svelte/store"
import { findComponent, findComponentPath } from "./componentUtils"
@ -8,6 +9,7 @@ import { RoleUtils } from "@budibase/frontend-core"
export const store = getFrontendStore()
export const automationStore = getAutomationStore()
export const themeStore = getThemeStore()
export const temporalStore = getTemporalStore()
export const selectedScreen = derived(store, $store => {
return $store.screens.find(screen => screen._id === $store.selectedScreenId)

View File

@ -0,0 +1,44 @@
import { createLocalStorageStore } from "@budibase/frontend-core"
import { get } from "svelte/store"
export const getTemporalStore = () => {
const initialValue = {}
//const appId = window["##BUDIBASE_APP_ID##"] || "app"
const localStorageKey = `${123}.bb-temporal`
const store = createLocalStorageStore(localStorageKey, initialValue)
const setExpiring = (key, data, duration) => {
const updated = {
...data,
expiry: Date.now() + duration * 1000,
}
store.update(state => ({
...state,
[key]: updated,
}))
}
const getExpiring = key => {
const entry = get(store)[key]
if (!entry) {
return
}
const currentExpiry = entry.expiry
if (currentExpiry < Date.now()) {
store.update(state => {
delete state[key]
return state
})
return null
} else {
return entry
}
}
return {
subscribe: store.subscribe,
actions: { setExpiring, getExpiring },
}
}

View File

@ -10,6 +10,7 @@
} from "@budibase/bbui"
import TemplateCard from "components/common/TemplateCard.svelte"
import CreateAppModal from "components/start/CreateAppModal.svelte"
import { licensing } from "stores/portal"
export let templates
@ -96,15 +97,17 @@
backgroundColour={templateEntry.background}
icon={templateEntry.icon}
>
<Button
cta
on:click={() => {
template = templateEntry
creationModal.show()
}}
>
Use template
</Button>
{#if $licensing?.usageMetrics?.apps < 100}
<Button
cta
on:click={() => {
template = templateEntry
creationModal.show()
}}
>
Use template
</Button>
{/if}
<a
href={templateEntry.url}
target="_blank"

View File

@ -0,0 +1,51 @@
<script>
import { Modal, ModalContent, Body } from "@budibase/bbui"
import { auth, admin } from "stores/portal"
export let onDismiss = () => {}
export let onShow = () => {}
let accountDowngradeModal
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
export function show() {
accountDowngradeModal.show()
}
export function hide() {
accountDowngradeModal.hide()
}
</script>
<Modal bind:this={accountDowngradeModal} on:show={onShow} on:hide={onDismiss}>
<ModalContent
title="Your account is now on the Free plan"
size="M"
showCancelButton={$auth.user.accountPortalAccess}
confirmText={$auth.user.accountPortalAccess ? "Upgrade" : "Confirm"}
onConfirm={$auth.user.accountPortalAccess
? () => {
window.location.href = upgradeUrl
}
: null}
>
<Body>
The payment for your Business Subscription failed and we have downgraded
your account to the <span class="free-plan">Free plan</span>.
</Body>
<Body>
Update to Business to get all your apps and user sessions back up and
running.
</Body>
{#if !$auth.user.accountPortalAccess}
<Body>Please contact the account holder.</Body>
{/if}
</ModalContent>
</Modal>
<style>
.free-plan {
font-weight: 600;
}
</style>

View File

@ -0,0 +1,46 @@
<script>
import { Modal, ModalContent, Body } from "@budibase/bbui"
import { auth, admin } from "stores/portal"
export let onDismiss = () => {}
let appLimitModal
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
export function show() {
appLimitModal.show()
}
export function hide() {
appLimitModal.hide()
}
</script>
<Modal bind:this={appLimitModal} on:hide={onDismiss}>
<ModalContent
title="Upgrade to get more apps"
size="M"
showCancelButton={false}
confirmText={$auth.user.accountPortalAccess ? "Upgrade" : "Confirm"}
onConfirm={$auth.user.accountPortalAccess
? () => {
window.location.href = upgradeUrl
}
: null}
>
<Body>
You are currently on our <span class="free-plan">Free plan</span>. Upgrade
to our Pro plan to get unlimited apps.
</Body>
{#if !$auth.user.accountPortalAccess}
<Body>Please contact the account holder.</Body>
{/if}
</ModalContent>
</Modal>
<style>
.free-plan {
font-weight: 600;
}
</style>

View File

@ -0,0 +1,54 @@
<script>
import { Modal, ModalContent, Body } from "@budibase/bbui"
import { licensing, auth, admin } from "stores/portal"
export let onDismiss = () => {}
export let onShow = () => {}
let sessionsModal
const outOfSessionsTitle = "You are almost out of sessions"
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
$: daysRemaining = $licensing.quotaResetDaysRemaining
$: sessionsUsed = $licensing.usageMetrics?.dayPasses
export function show() {
sessionsModal.show()
}
export function hide() {
sessionsModal.hide()
}
</script>
<Modal bind:this={sessionsModal} on:show={onShow} on:hide={onDismiss}>
{#if $auth.user.accountPortalAccess}
<ModalContent
title={outOfSessionsTitle}
size="M"
confirmText="Upgrade"
onConfirm={() => {
window.location.href = upgradeUrl
}}
>
<Body>
You have used <span class="session_percent">{sessionsUsed}%</span> of
your plans Day Passes with {daysRemaining} day{daysRemaining == 1
? ""
: "s"} remaining.
</Body>
<Body>Upgrade your account to prevent your apps from going offline.</Body>
</ModalContent>
{:else}
<ModalContent title={outOfSessionsTitle} size="M" showCancelButton={false}>
<Body>
You have used <span class="session_percent">{sessionsUsed}%</span> of
your plans Day Passes with {daysRemaining} day{daysRemaining == 1
? ""
: "s"} remaining.
</Body>
<Body>Please contact your account holder.</Body>
</ModalContent>
{/if}
</Modal>

View File

@ -0,0 +1,117 @@
<script>
import { licensing, auth } from "stores/portal"
import { temporalStore } from "builderStore"
import { onMount } from "svelte"
import DayPassWarningModal from "./DayPassWarningModal.svelte"
import PaymentFailedModal from "./PaymentFailedModal.svelte"
import AccountDowngradedModal from "./AccountDowngradedModal.svelte"
import { ExpiringKeys } from "./constants"
import { getBanners } from "./banners"
import { banner } from "@budibase/bbui"
const oneDayInSeconds = 86400
let queuedBanners = []
let queuedModals = []
let dayPassModal
let paymentFailedModal
let accountDowngradeModal
let userLoaded = false
let loaded = false
let licensingLoaded = false
let currentModalCfg = null
const processModals = () => {
const defaultCacheFn = key => {
temporalStore.actions.setExpiring(key, {}, oneDayInSeconds)
}
const dismissableModals = [
{
key: ExpiringKeys.LICENSING_DAYPASS_WARNING_MODAL,
criteria: () => {
return $licensing?.usageMetrics?.dayPasses >= 90
},
action: () => {
dayPassModal.show()
},
cache: () => {
defaultCacheFn(ExpiringKeys.LICENSING_DAYPASS_WARNING_MODAL)
},
},
{
key: ExpiringKeys.LICENSING_PAYMENT_FAILED,
criteria: () => {
return $licensing.accountPastDue
},
action: () => {
paymentFailedModal.show()
},
cache: () => {
defaultCacheFn(ExpiringKeys.LICENSING_PAYMENT_FAILED)
},
},
{
key: ExpiringKeys.LICENSING_ACCOUNT_DOWNGRADED_MODAL,
criteria: () => {
return $licensing?.accountDowngraded
},
action: () => {
accountDowngradeModal.show()
},
cache: () => {
defaultCacheFn(ExpiringKeys.LICENSING_ACCOUNT_DOWNGRADED_MODAL)
},
},
]
return dismissableModals.filter(modal => {
return !temporalStore.actions.getExpiring(modal.key) && modal.criteria()
})
}
$: if (userLoaded && licensingLoaded && loaded) {
queuedModals = processModals()
queuedBanners = getBanners()
showNext()
banner.queue(queuedBanners)
}
const showNext = () => {
if (currentModalCfg) {
currentModalCfg.cache()
}
if (queuedModals.length) {
currentModalCfg = queuedModals.shift()
currentModalCfg.action()
} else {
currentModalCfg = null
}
}
onMount(async () => {
auth.subscribe(state => {
if (state.user && !userLoaded) {
userLoaded = true
}
})
licensing.subscribe(state => {
if (state.usageMetrics && !licensingLoaded) {
licensingLoaded = true
}
})
temporalStore.subscribe(state => {
console.log("Stored temporal ", state)
})
loaded = true
})
</script>
<DayPassWarningModal bind:this={dayPassModal} onDismiss={showNext} />
<PaymentFailedModal bind:this={paymentFailedModal} onDismiss={showNext} />
<AccountDowngradedModal
bind:this={accountDowngradeModal}
onDismiss={showNext}
/>

View File

@ -0,0 +1,87 @@
<script>
import { Modal, ModalContent, Body, TooltipWrapper } from "@budibase/bbui"
import { auth, admin, licensing } from "stores/portal"
import { onMount } from "svelte"
export let onDismiss = () => {}
export let onShow = () => {}
let paymentFailedModal
let pastDueAt
const paymentFailedTitle = "Payment failed"
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
export function show() {
paymentFailedModal.show()
}
export function hide() {
paymentFailedModal.hide()
}
onMount(() => {
auth.subscribe(state => {
if (state.user && state.user.license?.billing?.subscription) {
pastDueAt = new Date(
state.user.license?.billing?.subscription.pastDueAt * 1000
)
}
})
})
</script>
<Modal bind:this={paymentFailedModal} on:show={onShow} on:hide={onDismiss}>
{#if $auth.user.accountPortalAccess}
<ModalContent
title={paymentFailedTitle}
size="M"
confirmText="Upgrade"
onConfirm={() => {
window.location.href = upgradeUrl
}}
>
<Body>The payment for your business plan subscription has failed</Body>
<Body>
Please upgrade your billing details before your account gets downgraded
to the free plan
</Body>
<Body weight={800}>
<div class="tooltip-root">
{`${$licensing.paymentDueDaysRemaining} day${
$licensing.paymentDueDaysRemaining == 1 ? "" : "s"
} remaining`}
<span class="tooltip">
<TooltipWrapper tooltip={pastDueAt.toString()} size="S" />
</span>
</div>
</Body>
</ModalContent>
{:else}
<ModalContent title={paymentFailedTitle} size="M" showCancelButton={false}>
<Body>The payment for your business plan subscription has failed</Body>
<Body>
Please upgrade your billing details before your account gets downgraded
to the free plan
</Body>
<Body>Please contact your account holder.</Body>
<Body weight={800}>
<div class="tooltip-root">
{`${$licensing.paymentDueDaysRemaining} day${
$licensing.paymentDueDaysRemaining == 1 ? "" : "s"
} remaining`}
<span class="tooltip">
<TooltipWrapper tooltip={pastDueAt.toString()} size="S" />
</span>
</div>
</Body>
</ModalContent>
{/if}
</Modal>
<style>
.tooltip-root {
display: flex;
align-items: center;
}
</style>

View File

@ -0,0 +1,132 @@
import { ExpiringKeys } from "./constants"
import { temporalStore } from "builderStore"
import { admin, auth, licensing } from "stores/portal"
import { get } from "svelte/store"
const oneDayInSeconds = 86400
const upgradeUrl = `${get(admin).accountPortalUrl}/portal/upgrade`
const defaultCacheFn = key => {
temporalStore.actions.setExpiring(key, {}, oneDayInSeconds)
}
const defaultAction = key => {
if (!get(auth).user.accountPortalAccess) {
return {}
}
return {
extraButtonText: "Upgrade Plan",
extraButtonAction: () => {
defaultCacheFn(key)
window.location.href = upgradeUrl
},
}
}
const buildUsageInfoBanner = (metricKey, metricLabel, cacheKey, percentage) => {
const appAuth = get(auth)
const appLicensing = get(licensing)
let bannerConfig = {
key: cacheKey,
type: "info",
onChange: () => {
defaultCacheFn(cacheKey)
},
message: `You have used ${
appLicensing?.usageMetrics[metricKey]
}% of your monthly usage of ${metricLabel} with ${
appLicensing.quotaResetDaysRemaining
} day${
appLicensing.quotaResetDaysRemaining == 1 ? "" : "s"
} remaining. All apps will be taken offline if this limit is reached. ${
appAuth.user.accountPortalAccess
? ""
: "Please contact your account holder."
}`,
criteria: () => {
return appLicensing?.usageMetrics[metricKey] >= percentage
},
priority: 0, //Banners.Priority 0, 1, 2 ??
}
return !get(auth).user.accountPortalAccess
? bannerConfig
: {
...bannerConfig,
...defaultAction(cacheKey),
}
}
const buildDayPassBanner = () => {
const appAuth = get(auth)
if (get(licensing)?.usageMetrics["dayPasses"] >= 100) {
return {
key: "max_dayPasses",
type: "negative",
criteria: () => {
return true
},
message: `Your apps are currently offline. You have exceeded your plans limit for Day Passes. ${
appAuth.user.accountPortalAccess
? ""
: "Please contact your account holder."
}`,
...defaultAction(),
showCloseButton: false,
}
}
return buildUsageInfoBanner(
"dayPasses",
"Day Passes",
ExpiringKeys.LICENSING_DAYPASS_WARNING_BANNER,
90
)
}
const buildPaymentFailedBanner = () => {
return {
key: "payment_Failed",
type: "negative",
criteria: () => {
return get(licensing)?.accountPastDue
},
message: `Payment Failed - Please update your billing details or your account will be downgrades in
${get(licensing)?.paymentDueDaysRemaining} day${
get(licensing)?.paymentDueDaysRemaining == 1 ? "" : "s"
}`,
...defaultAction(),
showCloseButton: false,
}
}
export const getBanners = () => {
return [
buildPaymentFailedBanner(),
buildDayPassBanner(ExpiringKeys.LICENSING_DAYPASS_WARNING_BANNER),
buildUsageInfoBanner(
"rows",
"Rows",
ExpiringKeys.LICENSING_ROWS_WARNING_BANNER,
90
),
buildUsageInfoBanner(
"automations",
"Automations",
ExpiringKeys.LICENSING_AUTOMATIONS_WARNING_BANNER,
90
),
buildUsageInfoBanner(
"queries",
"Queries",
ExpiringKeys.LICENSING_QUERIES_WARNING_BANNER,
90 // could be an array [50,75,90]
),
].filter(licensingBanner => {
return (
!temporalStore.actions.getExpiring(licensingBanner.key) &&
licensingBanner.criteria()
)
})
}

View File

@ -0,0 +1,15 @@
export const ExpiringKeys = {
LICENSING_DAYPASS_WARNING_MODAL: "licensing_daypass_warning_90_modal",
LICENSING_DAYPASS_WARNING_BANNER: "licensing_daypass_warning_90_banner",
LICENSING_PAYMENT_FAILED: "licensing_payment_failed",
LICENSING_ACCOUNT_DOWNGRADED_MODAL: "licensing_account_downgraded_modal",
LICENSING_APP_LIMIT_MODAL: "licensing_app_limit_modal",
LICENSING_ROWS_WARNING_BANNER: "licensing_rows_warning_banner",
LICENSING_AUTOMATIONS_WARNING_BANNER: "licensing_automations_warning_banner",
LICENSING_QUERIES_WARNING_BANNER: "licensing_automations_warning_banner",
}
export const StripeStatus = {
PAST_DUE: "past_due",
ACTIVE: "active",
}

View File

@ -1,6 +1,6 @@
<script>
import { isActive, redirect, params } from "@roxi/routify"
import { admin, auth } from "stores/portal"
import { admin, auth, licensing } from "stores/portal"
import { onMount } from "svelte"
import { CookieUtils, Constants } from "@budibase/frontend-core"
import { API } from "api"
@ -63,6 +63,9 @@
await auth.getSelf()
await admin.init()
await licensing.getQuotaUsage()
await licensing.getUsageMetrics()
// Set init info if present
if ($params["?template"]) {
await auth.setInitInfo({ init_template: $params["?template"] })

View File

@ -13,12 +13,14 @@
} from "@budibase/bbui"
import CreateAppModal from "components/start/CreateAppModal.svelte"
import TemplateDisplay from "components/common/TemplateDisplay.svelte"
import AppLimitModal from "components/portal/licensing/AppLimitModal.svelte"
import { onMount } from "svelte"
import { templates } from "stores/portal"
import { templates, licensing } from "stores/portal"
let loaded = $templates?.length
let template
let creationModal = false
let appLimitModal
let creatingApp = false
const welcomeBody =
@ -29,6 +31,8 @@
onMount(async () => {
try {
await templates.load()
await licensing.getQuotaUsage()
await licensing.getUsageMetrics()
if ($templates?.length === 0) {
notifications.error(
"There was a problem loading quick start templates."
@ -41,9 +45,13 @@
})
const initiateAppCreation = () => {
template = null
creationModal.show()
creatingApp = true
if ($licensing.usageMetrics.apps >= 100) {
appLimitModal.show()
} else {
template = null
creationModal.show()
creatingApp = true
}
}
const stopAppCreation = () => {
@ -52,9 +60,13 @@
}
const initiateAppImport = () => {
template = { fromFile: true }
creationModal.show()
creatingApp = true
if ($licensing.usageMetrics.apps >= 100) {
appLimitModal.show()
} else {
template = { fromFile: true }
creationModal.show()
creatingApp = true
}
}
</script>
@ -121,6 +133,7 @@
>
<CreateAppModal {template} />
</Modal>
<AppLimitModal bind:this={appLimitModal} />
<style>
.title .welcome > .buttons {

View File

@ -16,6 +16,7 @@
import CreateAppModal from "components/start/CreateAppModal.svelte"
import UpdateAppModal from "components/start/UpdateAppModal.svelte"
import ExportAppModal from "components/start/ExportAppModal.svelte"
import AppLimitModal from "components/portal/licensing/AppLimitModal.svelte"
import { store, automationStore } from "builderStore"
import { API } from "api"
@ -34,6 +35,7 @@
let creationModal
let updatingModal
let exportModal
let appLimitModal
let creatingApp = false
let loaded = $apps?.length || $templates?.length
let searchTerm = ""
@ -126,8 +128,10 @@
return `${app.name} - Automation error (${errorCount(errors)})`
}
const initiateAppCreation = () => {
if ($apps?.length) {
const initiateAppCreation = async () => {
if ($licensing.usageMetrics.apps >= 100) {
appLimitModal.show()
} else if ($apps?.length) {
$goto("/builder/portal/apps/create")
} else {
template = null
@ -227,6 +231,10 @@
try {
await apps.load()
await templates.load()
await licensing.getQuotaUsage()
await licensing.getUsageMetrics()
if ($templates?.length === 0) {
notifications.error(
"There was a problem loading quick start templates."
@ -243,10 +251,6 @@
notifications.error("Error loading apps and templates")
}
loaded = true
//Testing
await licensing.getQuotaUsage()
await licensing.getTestData()
})
</script>
@ -415,6 +419,8 @@
<ExportAppModal app={selectedApp} />
</Modal>
<AppLimitModal bind:this={appLimitModal} />
<style>
.appTable {
border-top: var(--border-light);

View File

@ -1,12 +1,16 @@
import { writable, get } from "svelte/store"
import { API } from "api"
import { auth } from "stores/portal"
import { Constants } from "@budibase/frontend-core"
import { StripeStatus } from "components/portal/licensing/constants"
export const createLicensingStore = () => {
const DEFAULT = {
plans: {},
}
const oneDayInMilliseconds = 86400000
const store = writable(DEFAULT)
const actions = {
@ -19,65 +23,86 @@ export const createLicensingStore = () => {
}
})
},
getTestData: async () => {
const tenantId = get(auth).tenantId
console.log("Tenant ", tenantId)
const license = get(auth).user.license
console.log("User LICENSE ", license)
// Pull out the usage.
getUsageMetrics: async () => {
const quota = get(store).quotaUsage
console.log("Quota usage", quota)
const license = get(auth).user.license
const now = Date.now()
const nowSeconds = now / 1000
// Would only initialise the usage elements if the account element is present.
console.log("User account ", get(auth).user.account)
const getMetrics = (keys, license, quota) => {
if (!license || !quota || !keys) {
return {}
}
return keys.reduce((acc, key) => {
const quotaLimit = license[key].value
const quotaUsed = (quota[key] / quotaLimit) * 100
//separate into functions that pass in both the usage and quota
//easier to test
// Catch for sessions
key = key === "sessions" ? "dayPasses" : key
const getMonthlyMetrics = (license, quota) => {
return ["sessions", "queries", "automations"].reduce((acc, key) => {
const quotaLimit = license.quotas.usage.monthly[key].value
acc[key] =
quotaLimit > -1
? (quota.monthly.current[key] / quotaLimit) * 100
: -1
acc[key] = quotaLimit > -1 ? Math.round(quotaUsed) : -1
return acc
}, {})
}
const monthlyMetrics = getMetrics(
["sessions", "queries", "automations"],
license.quotas.usage.monthly,
quota.monthly.current
)
const staticMetrics = getMetrics(
["apps", "rows"],
license.quotas.usage.static,
quota.usageQuota
)
const getStaticMetrics = (license, quota) => {
return ["apps", "rows"].reduce((acc, key) => {
const quotaLimit = license.quotas.usage.monthly[key].value
acc[key] =
quotaLimit > -1 ? (quota.usageQuota[key] / quotaLimit) * 100 : -1
return acc
}, {})
}
const modQuotaStr = JSON.stringify(quota)
const cloneQuota = JSON.parse(modQuotaStr)
cloneQuota.monthly.current.sessions = 4
const monthlyMetrics = getMonthlyMetrics(license, cloneQuota)
const staticMetrics = getStaticMetrics(license, cloneQuota)
console.log("Monthly Usage Metrics ", monthlyMetrics)
console.log("Static Usage Metrics ", staticMetrics)
const flagged = Object.keys(monthlyMetrics).filter(key => {
return monthlyMetrics[key] >= 100
// DEBUG
console.log("Store licensing val ", {
...monthlyMetrics,
...staticMetrics,
})
console.log(flagged)
let subscriptionDaysRemaining
if (license?.billing?.subscription) {
const currentPeriodEnd = license.billing.subscription.currentPeriodEnd
const currentPeriodEndMilliseconds = currentPeriodEnd * 1000
// store.update(state => {
// return {
// ...state,
// metrics,
// }
// })
subscriptionDaysRemaining = Math.round(
(currentPeriodEndMilliseconds - now) / oneDayInMilliseconds
)
}
const quotaResetDaysRemaining =
quota.quotaReset > now
? Math.round((quota.quotaReset - now) / oneDayInMilliseconds)
: 0
const accountDowngraded =
license?.billing?.subscription?.status === StripeStatus.PAST_DUE &&
license?.plan === Constants.PlanType.FREE
const accountPastDue =
nowSeconds >= license?.billing?.subscription?.currentPeriodEnd &&
nowSeconds <= license?.billing?.subscription?.pastDueAt &&
license?.billing?.subscription?.status === StripeStatus.PAST_DUE &&
!accountDowngraded
const pastDueAtSeconds = license?.billing?.subscription?.pastDueAt
const pastDueAtMilliseconds = pastDueAtSeconds * 1000
const paymentDueDaysRemaining = Math.round(
(pastDueAtMilliseconds - now) / oneDayInMilliseconds
)
store.update(state => {
return {
...state,
usageMetrics: { ...monthlyMetrics, ...staticMetrics },
subscriptionDaysRemaining,
paymentDueDaysRemaining,
quotaResetDaysRemaining,
accountDowngraded,
accountPastDue,
}
})
},
}