Apps Page update to hide apps when sessions are maxed. General refactoring and updates to the licensing notification flows.
This commit is contained in:
parent
6bd3c8c3d7
commit
132f347916
14 changed files with 198 additions and 139 deletions
|
@ -16,13 +16,15 @@
|
|||
extraButtonText={message.extraButtonText}
|
||||
extraButtonAction={message.extraButtonAction}
|
||||
on:change={() => {
|
||||
if (message.onChange) {
|
||||
message.onChange()
|
||||
}
|
||||
}}
|
||||
showCloseButton={typeof message.showCloseButton === "boolean"
|
||||
? message.showCloseButton
|
||||
: true}
|
||||
>
|
||||
<TooltipWrapper tooltip={"test"}>
|
||||
<TooltipWrapper tooltip={message.tooltip} disabled={false}>
|
||||
{message.message}
|
||||
</TooltipWrapper>
|
||||
</Banner>
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { writable } from "svelte/store"
|
||||
|
||||
export const BANNER_TYPES = {
|
||||
INFO: "info",
|
||||
NEGATIVE: "negative",
|
||||
}
|
||||
|
||||
export function createBannerStore() {
|
||||
const DEFAULT_CONFIG = {
|
||||
messages: [],
|
||||
|
@ -22,19 +27,26 @@ export function createBannerStore() {
|
|||
const showStatus = async () => {
|
||||
const config = {
|
||||
message: "Some systems are experiencing issues",
|
||||
type: "negative",
|
||||
type: BANNER_TYPES.NEGATIVE,
|
||||
extraButtonText: "View Status",
|
||||
extraButtonAction: () => window.open("https://status.budibase.com/"),
|
||||
}
|
||||
|
||||
await show(config)
|
||||
await queue([config])
|
||||
}
|
||||
|
||||
const queue = async entries => {
|
||||
const priority = {
|
||||
[BANNER_TYPES.NEGATIVE]: 0,
|
||||
[BANNER_TYPES.INFO]: 1,
|
||||
}
|
||||
banner.update(store => {
|
||||
const sorted = [...store.messages, ...entries].sort(
|
||||
(a, b) => a.priority > b.priority
|
||||
)
|
||||
const sorted = [...store.messages, ...entries].sort((a, b) => {
|
||||
if (priority[a.type] == priority[b.type]) {
|
||||
return 0
|
||||
}
|
||||
return priority[a.type] < priority[b.type] ? -1 : 1
|
||||
})
|
||||
return {
|
||||
...store,
|
||||
messages: sorted,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
export let tooltip = ""
|
||||
export let size = "M"
|
||||
export let disabled = true
|
||||
|
||||
let showTooltip = false
|
||||
</script>
|
||||
|
@ -19,7 +20,7 @@
|
|||
on:mouseleave={() => (showTooltip = false)}
|
||||
on:focus
|
||||
>
|
||||
<Icon name="InfoOutline" size="S" disabled={true} />
|
||||
<Icon name="InfoOutline" size="S" {disabled} />
|
||||
</div>
|
||||
{#if showTooltip}
|
||||
<div class="tooltip">
|
||||
|
|
|
@ -95,7 +95,7 @@ export { default as clickOutside } from "./Actions/click_outside"
|
|||
|
||||
// Stores
|
||||
export { notifications, createNotificationStore } from "./Stores/notifications"
|
||||
export { banner } from "./Stores/banner"
|
||||
export { banner, BANNER_TYPES } from "./Stores/banner"
|
||||
|
||||
// Helpers
|
||||
export * as Helpers from "./helpers"
|
||||
|
|
|
@ -4,8 +4,7 @@ import { get } from "svelte/store"
|
|||
export const getTemporalStore = () => {
|
||||
const initialValue = {}
|
||||
|
||||
//const appId = window["##BUDIBASE_APP_ID##"] || "app"
|
||||
const localStorageKey = `${123}.bb-temporal`
|
||||
const localStorageKey = `bb-temporal`
|
||||
const store = createLocalStorageStore(localStorageKey, initialValue)
|
||||
|
||||
const setExpiring = (key, data, duration) => {
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
let accountDowngradeModal
|
||||
|
||||
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
|
||||
$: accountUrl = $admin.accountPortalUrl
|
||||
$: upgradeUrl = `${accountUrl}/portal/upgrade`
|
||||
|
||||
export function show() {
|
||||
accountDowngradeModal.show()
|
||||
|
@ -31,15 +32,12 @@
|
|||
: 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.
|
||||
The payment for your subscription has failed and we have downgraded your
|
||||
account to the <span class="free-plan">Free plan</span>.
|
||||
</Body>
|
||||
<Body>Upgrade to restore full functionality.</Body>
|
||||
{#if !$auth.user.accountPortalAccess}
|
||||
<Body>Please contact the account holder.</Body>
|
||||
<Body>Please contact the account holder to upgrade.</Body>
|
||||
{/if}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
let appLimitModal
|
||||
|
||||
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
|
||||
$: accountUrl = $admin.accountPortalUrl
|
||||
$: upgradeUrl = `${accountUrl}/portal/upgrade`
|
||||
|
||||
export function show() {
|
||||
appLimitModal.show()
|
||||
|
@ -31,10 +32,10 @@
|
|||
>
|
||||
<Body>
|
||||
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>
|
||||
{#if !$auth.user.accountPortalAccess}
|
||||
<Body>Please contact the account holder.</Body>
|
||||
<Body>Please contact the account holder to upgrade.</Body>
|
||||
{/if}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
|
|
@ -1,31 +1,40 @@
|
|||
<script>
|
||||
import { Modal, ModalContent, Body } from "@budibase/bbui"
|
||||
import { Modal, ModalContent, Body, TooltipWrapper } from "@budibase/bbui"
|
||||
import { licensing, auth, admin } from "stores/portal"
|
||||
|
||||
export let onDismiss = () => {}
|
||||
export let onShow = () => {}
|
||||
|
||||
let sessionsModal
|
||||
let dayPassModal
|
||||
|
||||
const outOfSessionsTitle = "You are almost out of sessions"
|
||||
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
|
||||
$: accountUrl = $admin.accountPortalUrl
|
||||
$: upgradeUrl = `${accountUrl}/portal/upgrade`
|
||||
|
||||
$: 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() {
|
||||
sessionsModal.show()
|
||||
dayPassModal.show()
|
||||
}
|
||||
|
||||
export function hide() {
|
||||
sessionsModal.hide()
|
||||
dayPassModal.hide()
|
||||
}
|
||||
</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}
|
||||
<ModalContent
|
||||
title={outOfSessionsTitle}
|
||||
title={dayPassesTitle}
|
||||
size="M"
|
||||
confirmText="Upgrade"
|
||||
onConfirm={() => {
|
||||
|
@ -33,22 +42,37 @@
|
|||
}}
|
||||
>
|
||||
<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
|
||||
? ""
|
||||
: "s"} remaining.
|
||||
<span class="tooltip">
|
||||
<TooltipWrapper tooltip={quotaResetDate} size="S" />
|
||||
</span>
|
||||
</Body>
|
||||
<Body>Upgrade your account to prevent your apps from going offline.</Body>
|
||||
<Body>{dayPassesBody}</Body>
|
||||
</ModalContent>
|
||||
{:else}
|
||||
<ModalContent title={outOfSessionsTitle} size="M" showCancelButton={false}>
|
||||
<ModalContent title={dayPassesTitle} size="M" showCancelButton={false}>
|
||||
<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
|
||||
? ""
|
||||
: "s"} remaining.
|
||||
<span class="tooltip">
|
||||
<TooltipWrapper tooltip={quotaResetDate} size="S" />
|
||||
</span>
|
||||
</Body>
|
||||
<Body>Please contact your account holder.</Body>
|
||||
<Body>Please contact your account holder to upgrade.</Body>
|
||||
</ModalContent>
|
||||
{/if}
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.tooltip {
|
||||
display: inline-block;
|
||||
}
|
||||
.tooltip :global(.icon-container) {
|
||||
margin: 0px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import PaymentFailedModal from "./PaymentFailedModal.svelte"
|
||||
import AccountDowngradedModal from "./AccountDowngradedModal.svelte"
|
||||
import { ExpiringKeys } from "./constants"
|
||||
import { getBanners } from "./banners"
|
||||
import { getBanners } from "./licensingBanners"
|
||||
import { banner } from "@budibase/bbui"
|
||||
|
||||
const oneDayInSeconds = 86400
|
||||
|
@ -42,7 +42,7 @@
|
|||
{
|
||||
key: ExpiringKeys.LICENSING_PAYMENT_FAILED,
|
||||
criteria: () => {
|
||||
return $licensing.accountPastDue
|
||||
return $licensing.accountPastDue && !$licensing.isFreePlan()
|
||||
},
|
||||
action: () => {
|
||||
paymentFailedModal.show()
|
||||
|
@ -69,14 +69,7 @@
|
|||
})
|
||||
}
|
||||
|
||||
$: if (userLoaded && licensingLoaded && loaded) {
|
||||
queuedModals = processModals()
|
||||
queuedBanners = getBanners()
|
||||
showNext()
|
||||
banner.queue(queuedBanners)
|
||||
}
|
||||
|
||||
const showNext = () => {
|
||||
const showNextModal = () => {
|
||||
if (currentModalCfg) {
|
||||
currentModalCfg.cache()
|
||||
}
|
||||
|
@ -88,6 +81,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
$: if (userLoaded && licensingLoaded && loaded) {
|
||||
queuedModals = processModals()
|
||||
queuedBanners = getBanners()
|
||||
showNextModal()
|
||||
banner.queue(queuedBanners)
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
auth.subscribe(state => {
|
||||
if (state.user && !userLoaded) {
|
||||
|
@ -100,18 +100,13 @@
|
|||
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} />
|
||||
<DayPassWarningModal bind:this={dayPassModal} onDismiss={showNextModal} />
|
||||
<PaymentFailedModal bind:this={paymentFailedModal} onDismiss={showNextModal} />
|
||||
<AccountDowngradedModal
|
||||
bind:this={accountDowngradeModal}
|
||||
onDismiss={showNext}
|
||||
onDismiss={showNextModal}
|
||||
/>
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
export let onShow = () => {}
|
||||
|
||||
let paymentFailedModal
|
||||
let pastDueAt
|
||||
let pastDueEndDate
|
||||
|
||||
const paymentFailedTitle = "Payment failed"
|
||||
const upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade`
|
||||
$: accountUrl = $admin.accountPortalUrl
|
||||
$: upgradeUrl = `${accountUrl}/portal/upgrade`
|
||||
|
||||
export function show() {
|
||||
paymentFailedModal.show()
|
||||
|
@ -21,12 +22,8 @@
|
|||
}
|
||||
|
||||
onMount(() => {
|
||||
auth.subscribe(state => {
|
||||
if (state.user && state.user.license?.billing?.subscription) {
|
||||
pastDueAt = new Date(
|
||||
state.user.license?.billing?.subscription.pastDueAt * 1000
|
||||
)
|
||||
}
|
||||
licensing.subscribe(state => {
|
||||
pastDueEndDate = state.pastDueEndDate
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
@ -48,11 +45,11 @@
|
|||
</Body>
|
||||
<Body weight={800}>
|
||||
<div class="tooltip-root">
|
||||
{`${$licensing.paymentDueDaysRemaining} day${
|
||||
$licensing.paymentDueDaysRemaining == 1 ? "" : "s"
|
||||
{`${$licensing.pastDueDaysRemaining} day${
|
||||
$licensing.pastDueDaysRemaining == 1 ? "" : "s"
|
||||
} remaining`}
|
||||
<span class="tooltip">
|
||||
<TooltipWrapper tooltip={pastDueAt.toString()} size="S" />
|
||||
<TooltipWrapper tooltip={pastDueEndDate} size="S" />
|
||||
</span>
|
||||
</div>
|
||||
</Body>
|
||||
|
@ -67,11 +64,11 @@
|
|||
<Body>Please contact your account holder.</Body>
|
||||
<Body weight={800}>
|
||||
<div class="tooltip-root">
|
||||
{`${$licensing.paymentDueDaysRemaining} day${
|
||||
$licensing.paymentDueDaysRemaining == 1 ? "" : "s"
|
||||
{`${$licensing.pastDueDaysRemaining} day${
|
||||
$licensing.pastDueDaysRemaining == 1 ? "" : "s"
|
||||
} remaining`}
|
||||
<span class="tooltip">
|
||||
<TooltipWrapper tooltip={pastDueAt.toString()} size="S" />
|
||||
<TooltipWrapper tooltip={pastDueEndDate} size="S" />
|
||||
</span>
|
||||
</div>
|
||||
</Body>
|
||||
|
|
|
@ -6,7 +6,7 @@ export const ExpiringKeys = {
|
|||
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",
|
||||
LICENSING_QUERIES_WARNING_BANNER: "licensing_queries_warning_banner",
|
||||
}
|
||||
|
||||
export const StripeStatus = {
|
||||
|
|
|
@ -2,9 +2,9 @@ import { ExpiringKeys } from "./constants"
|
|||
import { temporalStore } from "builderStore"
|
||||
import { admin, auth, licensing } from "stores/portal"
|
||||
import { get } from "svelte/store"
|
||||
import { BANNER_TYPES } from "@budibase/bbui"
|
||||
|
||||
const oneDayInSeconds = 86400
|
||||
const upgradeUrl = `${get(admin).accountPortalUrl}/portal/upgrade`
|
||||
|
||||
const defaultCacheFn = key => {
|
||||
temporalStore.actions.setExpiring(key, {}, oneDayInSeconds)
|
||||
|
@ -18,36 +18,47 @@ const defaultAction = key => {
|
|||
extraButtonText: "Upgrade Plan",
|
||||
extraButtonAction: () => {
|
||||
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 appLicensing = get(licensing)
|
||||
|
||||
const displayPercent =
|
||||
appLicensing?.usageMetrics[metricKey] > 100
|
||||
? 100
|
||||
: appLicensing?.usageMetrics[metricKey]
|
||||
|
||||
let bannerConfig = {
|
||||
key: cacheKey,
|
||||
type: "info",
|
||||
type: BANNER_TYPES.INFO,
|
||||
onChange: () => {
|
||||
defaultCacheFn(cacheKey)
|
||||
},
|
||||
message: `You have used ${
|
||||
appLicensing?.usageMetrics[metricKey]
|
||||
}% of your monthly usage of ${metricLabel} with ${
|
||||
message: customMessage
|
||||
? customMessage
|
||||
: `You have used ${displayPercent}% 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. ${
|
||||
} remaining. ${
|
||||
appAuth.user.accountPortalAccess
|
||||
? ""
|
||||
: "Please contact your account holder."
|
||||
: "Please contact your account holder to upgrade"
|
||||
}`,
|
||||
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
|
||||
|
@ -60,17 +71,18 @@ const buildUsageInfoBanner = (metricKey, metricLabel, cacheKey, percentage) => {
|
|||
|
||||
const buildDayPassBanner = () => {
|
||||
const appAuth = get(auth)
|
||||
const appLicensing = get(licensing)
|
||||
if (get(licensing)?.usageMetrics["dayPasses"] >= 100) {
|
||||
return {
|
||||
key: "max_dayPasses",
|
||||
type: "negative",
|
||||
type: BANNER_TYPES.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."
|
||||
: "Please contact your account holder to upgrade."
|
||||
}`,
|
||||
...defaultAction(),
|
||||
showCloseButton: false,
|
||||
|
@ -81,23 +93,35 @@ const buildDayPassBanner = () => {
|
|||
"dayPasses",
|
||||
"Day Passes",
|
||||
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 = () => {
|
||||
return {
|
||||
key: "payment_Failed",
|
||||
type: "negative",
|
||||
type: BANNER_TYPES.NEGATIVE,
|
||||
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
|
||||
${get(licensing)?.paymentDueDaysRemaining} day${
|
||||
get(licensing)?.paymentDueDaysRemaining == 1 ? "" : "s"
|
||||
${get(licensing)?.pastDueDaysRemaining} day${
|
||||
get(licensing)?.pastDueDaysRemaining == 1 ? "" : "s"
|
||||
}`,
|
||||
...defaultAction(),
|
||||
showCloseButton: false,
|
||||
tooltip: get(licensing).pastDueEndDate,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +145,7 @@ export const getBanners = () => {
|
|||
"queries",
|
||||
"Queries",
|
||||
ExpiringKeys.LICENSING_QUERIES_WARNING_BANNER,
|
||||
90 // could be an array [50,75,90]
|
||||
90
|
||||
),
|
||||
].filter(licensingBanner => {
|
||||
return (
|
|
@ -13,13 +13,14 @@
|
|||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
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 { AppStatus } from "constants"
|
||||
import { gradient } from "actions"
|
||||
import UpdateUserInfoModal from "components/settings/UpdateUserInfoModal.svelte"
|
||||
import ChangePasswordModal from "components/settings/ChangePasswordModal.svelte"
|
||||
import { processStringSync } from "@budibase/string-templates"
|
||||
import Spaceman from "assets/bb-space-man.svg"
|
||||
import Logo from "assets/bb-emblem.svg"
|
||||
|
||||
let loaded = false
|
||||
|
@ -91,7 +92,7 @@
|
|||
<div class="content">
|
||||
<Layout noPadding>
|
||||
<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">
|
||||
<div slot="control" class="avatar">
|
||||
<Avatar
|
||||
|
@ -131,7 +132,17 @@
|
|||
</Body>
|
||||
</Layout>
|
||||
<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>
|
||||
<div class="group">
|
||||
<Layout gap="S" noPadding>
|
||||
|
@ -194,10 +205,13 @@
|
|||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
img {
|
||||
img.logo {
|
||||
width: 40px;
|
||||
margin-bottom: -12px;
|
||||
}
|
||||
img.spaceman {
|
||||
width: 100px;
|
||||
}
|
||||
.avatar {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
|
|
|
@ -8,7 +8,6 @@ export const createLicensingStore = () => {
|
|||
const DEFAULT = {
|
||||
plans: {},
|
||||
}
|
||||
|
||||
const oneDayInMilliseconds = 86400000
|
||||
|
||||
const store = writable(DEFAULT)
|
||||
|
@ -26,8 +25,7 @@ export const createLicensingStore = () => {
|
|||
getUsageMetrics: async () => {
|
||||
const quota = get(store).quotaUsage
|
||||
const license = get(auth).user.license
|
||||
const now = Date.now()
|
||||
const nowSeconds = now / 1000
|
||||
const now = new Date()
|
||||
|
||||
const getMetrics = (keys, license, quota) => {
|
||||
if (!license || !quota || !keys) {
|
||||
|
@ -36,16 +34,12 @@ export const createLicensingStore = () => {
|
|||
return keys.reduce((acc, key) => {
|
||||
const quotaLimit = license[key].value
|
||||
const quotaUsed = (quota[key] / quotaLimit) * 100
|
||||
|
||||
// Catch for sessions
|
||||
key = key === "sessions" ? "dayPasses" : key
|
||||
|
||||
acc[key] = quotaLimit > -1 ? Math.round(quotaUsed) : -1
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
const monthlyMetrics = getMetrics(
|
||||
["sessions", "queries", "automations"],
|
||||
["dayPasses", "queries", "automations"],
|
||||
license.quotas.usage.monthly,
|
||||
quota.monthly.current
|
||||
)
|
||||
|
@ -55,52 +49,50 @@ export const createLicensingStore = () => {
|
|||
quota.usageQuota
|
||||
)
|
||||
|
||||
// DEBUG
|
||||
console.log("Store licensing val ", {
|
||||
...monthlyMetrics,
|
||||
...staticMetrics,
|
||||
})
|
||||
|
||||
let subscriptionDaysRemaining
|
||||
if (license?.billing?.subscription) {
|
||||
const currentPeriodEnd = license.billing.subscription.currentPeriodEnd
|
||||
const currentPeriodEndMilliseconds = currentPeriodEnd * 1000
|
||||
|
||||
subscriptionDaysRemaining = Math.round(
|
||||
(currentPeriodEndMilliseconds - now) / oneDayInMilliseconds
|
||||
const getDaysBetween = (dateStart, dateEnd) => {
|
||||
return dateEnd > dateStart
|
||||
? Math.round(
|
||||
(dateEnd.getTime() - dateStart.getTime()) / oneDayInMilliseconds
|
||||
)
|
||||
: 0
|
||||
}
|
||||
|
||||
const quotaResetDaysRemaining =
|
||||
quota.quotaReset > now
|
||||
? Math.round((quota.quotaReset - now) / oneDayInMilliseconds)
|
||||
: 0
|
||||
const quotaResetDate = new Date(quota.quotaReset)
|
||||
const quotaResetDaysRemaining = getDaysBetween(now, quotaResetDate)
|
||||
|
||||
const accountDowngraded =
|
||||
license?.billing?.subscription?.downgradeAt &&
|
||||
license?.billing?.subscription?.downgradeAt <= now.getTime() &&
|
||||
license?.billing?.subscription?.status === StripeStatus.PAST_DUE &&
|
||||
license?.plan === Constants.PlanType.FREE
|
||||
license?.plan.type === Constants.PlanType.FREE
|
||||
|
||||
const accountPastDue =
|
||||
nowSeconds >= license?.billing?.subscription?.currentPeriodEnd &&
|
||||
nowSeconds <= license?.billing?.subscription?.pastDueAt &&
|
||||
license?.billing?.subscription?.status === StripeStatus.PAST_DUE &&
|
||||
!accountDowngraded
|
||||
const pastDueAtMilliseconds = license?.billing?.subscription?.pastDueAt
|
||||
const downgradeAtMilliseconds =
|
||||
license?.billing?.subscription?.downgradeAt
|
||||
let pastDueDaysRemaining
|
||||
let pastDueEndDate
|
||||
|
||||
const pastDueAtSeconds = license?.billing?.subscription?.pastDueAt
|
||||
const pastDueAtMilliseconds = pastDueAtSeconds * 1000
|
||||
const paymentDueDaysRemaining = Math.round(
|
||||
(pastDueAtMilliseconds - now) / oneDayInMilliseconds
|
||||
if (pastDueAtMilliseconds && downgradeAtMilliseconds) {
|
||||
pastDueEndDate = new Date(downgradeAtMilliseconds)
|
||||
pastDueDaysRemaining = getDaysBetween(
|
||||
new Date(pastDueAtMilliseconds),
|
||||
pastDueEndDate
|
||||
)
|
||||
}
|
||||
|
||||
store.update(state => {
|
||||
return {
|
||||
...state,
|
||||
usageMetrics: { ...monthlyMetrics, ...staticMetrics },
|
||||
subscriptionDaysRemaining,
|
||||
paymentDueDaysRemaining,
|
||||
quotaResetDaysRemaining,
|
||||
quotaResetDate,
|
||||
accountDowngraded,
|
||||
accountPastDue,
|
||||
accountPastDue: pastDueAtMilliseconds != null,
|
||||
pastDueEndDate,
|
||||
pastDueDaysRemaining,
|
||||
isFreePlan: () => {
|
||||
return license?.plan.type === Constants.PlanType.FREE
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue