1
0
Fork 0
mirror of synced 2024-08-17 11:01:26 +12:00

Apps Page update to hide apps when sessions are maxed. General refactoring and updates to the licensing notification flows.

This commit is contained in:
Dean 2022-09-14 18:04:58 +01:00
parent 6bd3c8c3d7
commit 132f347916
14 changed files with 198 additions and 139 deletions

View file

@ -16,13 +16,15 @@
extraButtonText={message.extraButtonText} extraButtonText={message.extraButtonText}
extraButtonAction={message.extraButtonAction} extraButtonAction={message.extraButtonAction}
on:change={() => { on:change={() => {
message.onChange() if (message.onChange) {
message.onChange()
}
}} }}
showCloseButton={typeof message.showCloseButton === "boolean" showCloseButton={typeof message.showCloseButton === "boolean"
? message.showCloseButton ? message.showCloseButton
: true} : true}
> >
<TooltipWrapper tooltip={"test"}> <TooltipWrapper tooltip={message.tooltip} disabled={false}>
{message.message} {message.message}
</TooltipWrapper> </TooltipWrapper>
</Banner> </Banner>

View file

@ -1,5 +1,10 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
export const BANNER_TYPES = {
INFO: "info",
NEGATIVE: "negative",
}
export function createBannerStore() { export function createBannerStore() {
const DEFAULT_CONFIG = { const DEFAULT_CONFIG = {
messages: [], messages: [],
@ -22,19 +27,26 @@ export function createBannerStore() {
const showStatus = async () => { const showStatus = async () => {
const config = { const config = {
message: "Some systems are experiencing issues", message: "Some systems are experiencing issues",
type: "negative", type: BANNER_TYPES.NEGATIVE,
extraButtonText: "View Status", extraButtonText: "View Status",
extraButtonAction: () => window.open("https://status.budibase.com/"), extraButtonAction: () => window.open("https://status.budibase.com/"),
} }
await show(config) await queue([config])
} }
const queue = async entries => { const queue = async entries => {
const priority = {
[BANNER_TYPES.NEGATIVE]: 0,
[BANNER_TYPES.INFO]: 1,
}
banner.update(store => { banner.update(store => {
const sorted = [...store.messages, ...entries].sort( const sorted = [...store.messages, ...entries].sort((a, b) => {
(a, b) => a.priority > b.priority if (priority[a.type] == priority[b.type]) {
) return 0
}
return priority[a.type] < priority[b.type] ? -1 : 1
})
return { return {
...store, ...store,
messages: sorted, messages: sorted,

View file

@ -4,6 +4,7 @@
export let tooltip = "" export let tooltip = ""
export let size = "M" export let size = "M"
export let disabled = true
let showTooltip = false let showTooltip = false
</script> </script>
@ -19,7 +20,7 @@
on:mouseleave={() => (showTooltip = false)} on:mouseleave={() => (showTooltip = false)}
on:focus on:focus
> >
<Icon name="InfoOutline" size="S" disabled={true} /> <Icon name="InfoOutline" size="S" {disabled} />
</div> </div>
{#if showTooltip} {#if showTooltip}
<div class="tooltip"> <div class="tooltip">

View file

@ -95,7 +95,7 @@ export { default as clickOutside } from "./Actions/click_outside"
// Stores // Stores
export { notifications, createNotificationStore } from "./Stores/notifications" export { notifications, createNotificationStore } from "./Stores/notifications"
export { banner } from "./Stores/banner" export { banner, BANNER_TYPES } from "./Stores/banner"
// Helpers // Helpers
export * as Helpers from "./helpers" export * as Helpers from "./helpers"

View file

@ -4,8 +4,7 @@ import { get } from "svelte/store"
export const getTemporalStore = () => { export const getTemporalStore = () => {
const initialValue = {} const initialValue = {}
//const appId = window["##BUDIBASE_APP_ID##"] || "app" const localStorageKey = `bb-temporal`
const localStorageKey = `${123}.bb-temporal`
const store = createLocalStorageStore(localStorageKey, initialValue) const store = createLocalStorageStore(localStorageKey, initialValue)
const setExpiring = (key, data, duration) => { const setExpiring = (key, data, duration) => {

View file

@ -7,7 +7,8 @@
let accountDowngradeModal let accountDowngradeModal
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade` $: accountUrl = $admin.accountPortalUrl
$: upgradeUrl = `${accountUrl}/portal/upgrade`
export function show() { export function show() {
accountDowngradeModal.show() accountDowngradeModal.show()
@ -31,15 +32,12 @@
: null} : null}
> >
<Body> <Body>
The payment for your Business Subscription failed and we have downgraded The payment for your subscription has failed and we have downgraded your
your account to the <span class="free-plan">Free plan</span>. 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> </Body>
<Body>Upgrade to restore full functionality.</Body>
{#if !$auth.user.accountPortalAccess} {#if !$auth.user.accountPortalAccess}
<Body>Please contact the account holder.</Body> <Body>Please contact the account holder to upgrade.</Body>
{/if} {/if}
</ModalContent> </ModalContent>
</Modal> </Modal>

View file

@ -6,7 +6,8 @@
let appLimitModal let appLimitModal
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade` $: accountUrl = $admin.accountPortalUrl
$: upgradeUrl = `${accountUrl}/portal/upgrade`
export function show() { export function show() {
appLimitModal.show() appLimitModal.show()
@ -19,7 +20,7 @@
<Modal bind:this={appLimitModal} on:hide={onDismiss}> <Modal bind:this={appLimitModal} on:hide={onDismiss}>
<ModalContent <ModalContent
title="Upgrade to get more apps" title="Upgrade to get more apps "
size="M" size="M"
showCancelButton={false} showCancelButton={false}
confirmText={$auth.user.accountPortalAccess ? "Upgrade" : "Confirm"} confirmText={$auth.user.accountPortalAccess ? "Upgrade" : "Confirm"}
@ -31,10 +32,10 @@
> >
<Body> <Body>
You are currently on our <span class="free-plan">Free plan</span>. Upgrade You are currently on our <span class="free-plan">Free plan</span>. Upgrade
to our Pro plan to get unlimited apps. to our Pro plan to get unlimited apps and additional features.
</Body> </Body>
{#if !$auth.user.accountPortalAccess} {#if !$auth.user.accountPortalAccess}
<Body>Please contact the account holder.</Body> <Body>Please contact the account holder to upgrade.</Body>
{/if} {/if}
</ModalContent> </ModalContent>
</Modal> </Modal>

View file

@ -1,31 +1,40 @@
<script> <script>
import { Modal, ModalContent, Body } from "@budibase/bbui" import { Modal, ModalContent, Body, TooltipWrapper } from "@budibase/bbui"
import { licensing, auth, admin } from "stores/portal" import { licensing, auth, admin } from "stores/portal"
export let onDismiss = () => {} export let onDismiss = () => {}
export let onShow = () => {} export let onShow = () => {}
let sessionsModal let dayPassModal
const outOfSessionsTitle = "You are almost out of sessions" $: accountUrl = $admin.accountPortalUrl
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade` $: upgradeUrl = `${accountUrl}/portal/upgrade`
$: daysRemaining = $licensing.quotaResetDaysRemaining $: daysRemaining = $licensing.quotaResetDaysRemaining
$: sessionsUsed = $licensing.usageMetrics?.dayPasses $: quotaResetDate = $licensing.quotaResetDate
$: dayPassesUsed = $licensing.usageMetrics?.dayPasses
$: dayPassesTitle =
dayPassesUsed >= 100
? "You have run out of Day Passes"
: "You are almost out of Day Passes"
$: dayPassesBody =
dayPassesUsed >= 100
? "Upgrade your account to bring your apps back online."
: "Upgrade your account to prevent your apps from going offline."
export function show() { export function show() {
sessionsModal.show() dayPassModal.show()
} }
export function hide() { export function hide() {
sessionsModal.hide() dayPassModal.hide()
} }
</script> </script>
<Modal bind:this={sessionsModal} on:show={onShow} on:hide={onDismiss}> <Modal bind:this={dayPassModal} on:show={onShow} on:hide={onDismiss}>
{#if $auth.user.accountPortalAccess} {#if $auth.user.accountPortalAccess}
<ModalContent <ModalContent
title={outOfSessionsTitle} title={dayPassesTitle}
size="M" size="M"
confirmText="Upgrade" confirmText="Upgrade"
onConfirm={() => { onConfirm={() => {
@ -33,22 +42,37 @@
}} }}
> >
<Body> <Body>
You have used <span class="session_percent">{sessionsUsed}%</span> of You have used <span class="daypass_percent">{dayPassesUsed}%</span> of
your plans Day Passes with {daysRemaining} day{daysRemaining == 1 your plans Day Passes with {daysRemaining} day{daysRemaining == 1
? "" ? ""
: "s"} remaining. : "s"} remaining.
<span class="tooltip">
<TooltipWrapper tooltip={quotaResetDate} size="S" />
</span>
</Body> </Body>
<Body>Upgrade your account to prevent your apps from going offline.</Body> <Body>{dayPassesBody}</Body>
</ModalContent> </ModalContent>
{:else} {:else}
<ModalContent title={outOfSessionsTitle} size="M" showCancelButton={false}> <ModalContent title={dayPassesTitle} size="M" showCancelButton={false}>
<Body> <Body>
You have used <span class="session_percent">{sessionsUsed}%</span> of You have used <span class="daypass_percent">{dayPassesUsed}%</span> of
your plans Day Passes with {daysRemaining} day{daysRemaining == 1 your plans Day Passes with {daysRemaining} day{daysRemaining == 1
? "" ? ""
: "s"} remaining. : "s"} remaining.
<span class="tooltip">
<TooltipWrapper tooltip={quotaResetDate} size="S" />
</span>
</Body> </Body>
<Body>Please contact your account holder.</Body> <Body>Please contact your account holder to upgrade.</Body>
</ModalContent> </ModalContent>
{/if} {/if}
</Modal> </Modal>
<style>
.tooltip {
display: inline-block;
}
.tooltip :global(.icon-container) {
margin: 0px;
}
</style>

View file

@ -6,7 +6,7 @@
import PaymentFailedModal from "./PaymentFailedModal.svelte" import PaymentFailedModal from "./PaymentFailedModal.svelte"
import AccountDowngradedModal from "./AccountDowngradedModal.svelte" import AccountDowngradedModal from "./AccountDowngradedModal.svelte"
import { ExpiringKeys } from "./constants" import { ExpiringKeys } from "./constants"
import { getBanners } from "./banners" import { getBanners } from "./licensingBanners"
import { banner } from "@budibase/bbui" import { banner } from "@budibase/bbui"
const oneDayInSeconds = 86400 const oneDayInSeconds = 86400
@ -42,7 +42,7 @@
{ {
key: ExpiringKeys.LICENSING_PAYMENT_FAILED, key: ExpiringKeys.LICENSING_PAYMENT_FAILED,
criteria: () => { criteria: () => {
return $licensing.accountPastDue return $licensing.accountPastDue && !$licensing.isFreePlan()
}, },
action: () => { action: () => {
paymentFailedModal.show() paymentFailedModal.show()
@ -69,14 +69,7 @@
}) })
} }
$: if (userLoaded && licensingLoaded && loaded) { const showNextModal = () => {
queuedModals = processModals()
queuedBanners = getBanners()
showNext()
banner.queue(queuedBanners)
}
const showNext = () => {
if (currentModalCfg) { if (currentModalCfg) {
currentModalCfg.cache() currentModalCfg.cache()
} }
@ -88,6 +81,13 @@
} }
} }
$: if (userLoaded && licensingLoaded && loaded) {
queuedModals = processModals()
queuedBanners = getBanners()
showNextModal()
banner.queue(queuedBanners)
}
onMount(async () => { onMount(async () => {
auth.subscribe(state => { auth.subscribe(state => {
if (state.user && !userLoaded) { if (state.user && !userLoaded) {
@ -100,18 +100,13 @@
licensingLoaded = true licensingLoaded = true
} }
}) })
temporalStore.subscribe(state => {
console.log("Stored temporal ", state)
})
loaded = true loaded = true
}) })
</script> </script>
<DayPassWarningModal bind:this={dayPassModal} onDismiss={showNext} /> <DayPassWarningModal bind:this={dayPassModal} onDismiss={showNextModal} />
<PaymentFailedModal bind:this={paymentFailedModal} onDismiss={showNext} /> <PaymentFailedModal bind:this={paymentFailedModal} onDismiss={showNextModal} />
<AccountDowngradedModal <AccountDowngradedModal
bind:this={accountDowngradeModal} bind:this={accountDowngradeModal}
onDismiss={showNext} onDismiss={showNextModal}
/> />

View file

@ -7,10 +7,11 @@
export let onShow = () => {} export let onShow = () => {}
let paymentFailedModal let paymentFailedModal
let pastDueAt let pastDueEndDate
const paymentFailedTitle = "Payment failed" const paymentFailedTitle = "Payment failed"
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade` $: accountUrl = $admin.accountPortalUrl
$: upgradeUrl = `${accountUrl}/portal/upgrade`
export function show() { export function show() {
paymentFailedModal.show() paymentFailedModal.show()
@ -21,12 +22,8 @@
} }
onMount(() => { onMount(() => {
auth.subscribe(state => { licensing.subscribe(state => {
if (state.user && state.user.license?.billing?.subscription) { pastDueEndDate = state.pastDueEndDate
pastDueAt = new Date(
state.user.license?.billing?.subscription.pastDueAt * 1000
)
}
}) })
}) })
</script> </script>
@ -48,11 +45,11 @@
</Body> </Body>
<Body weight={800}> <Body weight={800}>
<div class="tooltip-root"> <div class="tooltip-root">
{`${$licensing.paymentDueDaysRemaining} day${ {`${$licensing.pastDueDaysRemaining} day${
$licensing.paymentDueDaysRemaining == 1 ? "" : "s" $licensing.pastDueDaysRemaining == 1 ? "" : "s"
} remaining`} } remaining`}
<span class="tooltip"> <span class="tooltip">
<TooltipWrapper tooltip={pastDueAt.toString()} size="S" /> <TooltipWrapper tooltip={pastDueEndDate} size="S" />
</span> </span>
</div> </div>
</Body> </Body>
@ -67,11 +64,11 @@
<Body>Please contact your account holder.</Body> <Body>Please contact your account holder.</Body>
<Body weight={800}> <Body weight={800}>
<div class="tooltip-root"> <div class="tooltip-root">
{`${$licensing.paymentDueDaysRemaining} day${ {`${$licensing.pastDueDaysRemaining} day${
$licensing.paymentDueDaysRemaining == 1 ? "" : "s" $licensing.pastDueDaysRemaining == 1 ? "" : "s"
} remaining`} } remaining`}
<span class="tooltip"> <span class="tooltip">
<TooltipWrapper tooltip={pastDueAt.toString()} size="S" /> <TooltipWrapper tooltip={pastDueEndDate} size="S" />
</span> </span>
</div> </div>
</Body> </Body>

View file

@ -6,7 +6,7 @@ export const ExpiringKeys = {
LICENSING_APP_LIMIT_MODAL: "licensing_app_limit_modal", LICENSING_APP_LIMIT_MODAL: "licensing_app_limit_modal",
LICENSING_ROWS_WARNING_BANNER: "licensing_rows_warning_banner", LICENSING_ROWS_WARNING_BANNER: "licensing_rows_warning_banner",
LICENSING_AUTOMATIONS_WARNING_BANNER: "licensing_automations_warning_banner", LICENSING_AUTOMATIONS_WARNING_BANNER: "licensing_automations_warning_banner",
LICENSING_QUERIES_WARNING_BANNER: "licensing_automations_warning_banner", LICENSING_QUERIES_WARNING_BANNER: "licensing_queries_warning_banner",
} }
export const StripeStatus = { export const StripeStatus = {

View file

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

View file

@ -13,13 +13,14 @@
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { onMount } from "svelte" import { onMount } from "svelte"
import { apps, organisation, auth, groups } from "stores/portal" import { apps, organisation, auth, groups, licensing } from "stores/portal"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { AppStatus } from "constants" import { AppStatus } from "constants"
import { gradient } from "actions" import { gradient } from "actions"
import UpdateUserInfoModal from "components/settings/UpdateUserInfoModal.svelte" import UpdateUserInfoModal from "components/settings/UpdateUserInfoModal.svelte"
import ChangePasswordModal from "components/settings/ChangePasswordModal.svelte" import ChangePasswordModal from "components/settings/ChangePasswordModal.svelte"
import { processStringSync } from "@budibase/string-templates" import { processStringSync } from "@budibase/string-templates"
import Spaceman from "assets/bb-space-man.svg"
import Logo from "assets/bb-emblem.svg" import Logo from "assets/bb-emblem.svg"
let loaded = false let loaded = false
@ -91,7 +92,7 @@
<div class="content"> <div class="content">
<Layout noPadding> <Layout noPadding>
<div class="header"> <div class="header">
<img alt="logo" src={$organisation.logoUrl || Logo} /> <img class="logo" alt="logo" src={$organisation.logoUrl || Logo} />
<ActionMenu align="right" dataCy="user-menu"> <ActionMenu align="right" dataCy="user-menu">
<div slot="control" class="avatar"> <div slot="control" class="avatar">
<Avatar <Avatar
@ -131,7 +132,17 @@
</Body> </Body>
</Layout> </Layout>
<Divider /> <Divider />
{#if userApps.length} {#if $licensing.usageMetrics.dayPasses >= 100}
<div>
<Layout gap="S" justifyItems="center">
<img class="spaceman" alt="spaceman" src={Spaceman} />
<Heading size="M">
{"Your apps are currently offline."}
</Heading>
Please contact the account holder to get them back online.
</Layout>
</div>
{:else if userApps.length}
<Heading>Apps</Heading> <Heading>Apps</Heading>
<div class="group"> <div class="group">
<Layout gap="S" noPadding> <Layout gap="S" noPadding>
@ -194,10 +205,13 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
img { img.logo {
width: 40px; width: 40px;
margin-bottom: -12px; margin-bottom: -12px;
} }
img.spaceman {
width: 100px;
}
.avatar { .avatar {
display: grid; display: grid;
grid-template-columns: auto auto; grid-template-columns: auto auto;

View file

@ -8,7 +8,6 @@ export const createLicensingStore = () => {
const DEFAULT = { const DEFAULT = {
plans: {}, plans: {},
} }
const oneDayInMilliseconds = 86400000 const oneDayInMilliseconds = 86400000
const store = writable(DEFAULT) const store = writable(DEFAULT)
@ -26,8 +25,7 @@ export const createLicensingStore = () => {
getUsageMetrics: async () => { getUsageMetrics: async () => {
const quota = get(store).quotaUsage const quota = get(store).quotaUsage
const license = get(auth).user.license const license = get(auth).user.license
const now = Date.now() const now = new Date()
const nowSeconds = now / 1000
const getMetrics = (keys, license, quota) => { const getMetrics = (keys, license, quota) => {
if (!license || !quota || !keys) { if (!license || !quota || !keys) {
@ -36,16 +34,12 @@ export const createLicensingStore = () => {
return keys.reduce((acc, key) => { return keys.reduce((acc, key) => {
const quotaLimit = license[key].value const quotaLimit = license[key].value
const quotaUsed = (quota[key] / quotaLimit) * 100 const quotaUsed = (quota[key] / quotaLimit) * 100
// Catch for sessions
key = key === "sessions" ? "dayPasses" : key
acc[key] = quotaLimit > -1 ? Math.round(quotaUsed) : -1 acc[key] = quotaLimit > -1 ? Math.round(quotaUsed) : -1
return acc return acc
}, {}) }, {})
} }
const monthlyMetrics = getMetrics( const monthlyMetrics = getMetrics(
["sessions", "queries", "automations"], ["dayPasses", "queries", "automations"],
license.quotas.usage.monthly, license.quotas.usage.monthly,
quota.monthly.current quota.monthly.current
) )
@ -55,52 +49,50 @@ export const createLicensingStore = () => {
quota.usageQuota quota.usageQuota
) )
// DEBUG const getDaysBetween = (dateStart, dateEnd) => {
console.log("Store licensing val ", { return dateEnd > dateStart
...monthlyMetrics, ? Math.round(
...staticMetrics, (dateEnd.getTime() - dateStart.getTime()) / oneDayInMilliseconds
}) )
: 0
let subscriptionDaysRemaining
if (license?.billing?.subscription) {
const currentPeriodEnd = license.billing.subscription.currentPeriodEnd
const currentPeriodEndMilliseconds = currentPeriodEnd * 1000
subscriptionDaysRemaining = Math.round(
(currentPeriodEndMilliseconds - now) / oneDayInMilliseconds
)
} }
const quotaResetDaysRemaining = const quotaResetDate = new Date(quota.quotaReset)
quota.quotaReset > now const quotaResetDaysRemaining = getDaysBetween(now, quotaResetDate)
? Math.round((quota.quotaReset - now) / oneDayInMilliseconds)
: 0
const accountDowngraded = const accountDowngraded =
license?.billing?.subscription?.downgradeAt &&
license?.billing?.subscription?.downgradeAt <= now.getTime() &&
license?.billing?.subscription?.status === StripeStatus.PAST_DUE && license?.billing?.subscription?.status === StripeStatus.PAST_DUE &&
license?.plan === Constants.PlanType.FREE license?.plan.type === Constants.PlanType.FREE
const accountPastDue = const pastDueAtMilliseconds = license?.billing?.subscription?.pastDueAt
nowSeconds >= license?.billing?.subscription?.currentPeriodEnd && const downgradeAtMilliseconds =
nowSeconds <= license?.billing?.subscription?.pastDueAt && license?.billing?.subscription?.downgradeAt
license?.billing?.subscription?.status === StripeStatus.PAST_DUE && let pastDueDaysRemaining
!accountDowngraded let pastDueEndDate
const pastDueAtSeconds = license?.billing?.subscription?.pastDueAt if (pastDueAtMilliseconds && downgradeAtMilliseconds) {
const pastDueAtMilliseconds = pastDueAtSeconds * 1000 pastDueEndDate = new Date(downgradeAtMilliseconds)
const paymentDueDaysRemaining = Math.round( pastDueDaysRemaining = getDaysBetween(
(pastDueAtMilliseconds - now) / oneDayInMilliseconds new Date(pastDueAtMilliseconds),
) pastDueEndDate
)
}
store.update(state => { store.update(state => {
return { return {
...state, ...state,
usageMetrics: { ...monthlyMetrics, ...staticMetrics }, usageMetrics: { ...monthlyMetrics, ...staticMetrics },
subscriptionDaysRemaining,
paymentDueDaysRemaining,
quotaResetDaysRemaining, quotaResetDaysRemaining,
quotaResetDate,
accountDowngraded, accountDowngraded,
accountPastDue, accountPastDue: pastDueAtMilliseconds != null,
pastDueEndDate,
pastDueDaysRemaining,
isFreePlan: () => {
return license?.plan.type === Constants.PlanType.FREE
},
} }
}) })
}, },