Porting UI, still needs testing.
This commit is contained in:
parent
c3056d8b9c
commit
1d950bed7d
|
@ -0,0 +1,55 @@
|
|||
<script>
|
||||
import { Body, ProgressBar, Label } from "@budibase/bbui"
|
||||
import { onMount } from "svelte"
|
||||
export let usage
|
||||
|
||||
let percentage
|
||||
let unlimited = false
|
||||
|
||||
const isUnlimited = () => {
|
||||
if (usage.total === -1) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const getPercentage = () => {
|
||||
return (usage.used / usage.total) * 100
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
unlimited = isUnlimited()
|
||||
percentage = getPercentage()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="usage">
|
||||
<div class="info">
|
||||
<Label size="XL">{usage.name}</Label>
|
||||
{#if unlimited}
|
||||
<Body size="S">{usage.used}</Body>
|
||||
{:else}
|
||||
<Body size="S">{usage.used} / {usage.total}</Body>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
{#if unlimited}
|
||||
<Body size="S">Unlimited</Body>
|
||||
{:else}
|
||||
<ProgressBar width={"100%"} duration={1} value={percentage} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.usage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
|
@ -61,6 +61,10 @@
|
|||
])
|
||||
|
||||
if (isEnabled(FEATURE_FLAGS.LICENSING)) {
|
||||
menu = menu.concat({
|
||||
title: "Billing",
|
||||
href: "/builder/portal/settings/billing",
|
||||
})
|
||||
menu = menu.concat({
|
||||
title: "Upgrade",
|
||||
href: "/builder/portal/settings/upgrade",
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
<script>
|
||||
import {
|
||||
Body,
|
||||
Divider,
|
||||
Heading,
|
||||
Layout,
|
||||
notifications,
|
||||
Page,
|
||||
} from "@budibase/bbui"
|
||||
import { onMount } from "svelte"
|
||||
import { auth, licensing } from "stores/portal"
|
||||
import Usage from "components/billing/Usage.svelte"
|
||||
|
||||
let staticUsage = []
|
||||
let monthlyUsage = []
|
||||
let loaded = false
|
||||
|
||||
$: quotaUsage = $licensing.quotaUsage
|
||||
$: license = $auth.user?.license
|
||||
|
||||
const setMonthlyUsage = () => {
|
||||
monthlyUsage = []
|
||||
if (quotaUsage.monthly) {
|
||||
for (let [key, value] of Object.entries(license.quotas.usage.monthly)) {
|
||||
const used = quotaUsage.monthly.current[key]
|
||||
if (used !== undefined) {
|
||||
monthlyUsage.push({
|
||||
name: value.name,
|
||||
used: used,
|
||||
total: value.value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setStaticUsage = () => {
|
||||
staticUsage = []
|
||||
for (let [key, value] of Object.entries(license.quotas.usage.static)) {
|
||||
const used = quotaUsage.usageQuota[key]
|
||||
if (used !== undefined) {
|
||||
staticUsage.push({
|
||||
name: value.name,
|
||||
used: used,
|
||||
total: value.value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const capitalise = string => {
|
||||
if (string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||
}
|
||||
}
|
||||
|
||||
const init = async () => {
|
||||
try {
|
||||
await licensing.getQuotaUsage()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
notifications.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
await init()
|
||||
loaded = true
|
||||
})
|
||||
|
||||
$: {
|
||||
if (license && quotaUsage) {
|
||||
setMonthlyUsage()
|
||||
setStaticUsage()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Page maxWidth={"100ch"}>
|
||||
{#if loaded}
|
||||
<Layout>
|
||||
<Heading>Billing</Heading>
|
||||
<Body>Get information about your current usage and manage your plan</Body>
|
||||
</Layout>
|
||||
<Layout gap="S">
|
||||
<Divider />
|
||||
</Layout>
|
||||
<Layout gap="S" noPadding>
|
||||
<Layout gap="XS">
|
||||
<Body size="S">Your plan</Body>
|
||||
<Heading size="S">{capitalise(license?.plan.type)}</Heading>
|
||||
</Layout>
|
||||
<Layout gap="S">
|
||||
<Body size="S">Usage</Body>
|
||||
<div class="usages">
|
||||
{#each staticUsage as usage}
|
||||
<div class="usage">
|
||||
<Usage {usage} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Layout>
|
||||
{#if monthlyUsage.length}
|
||||
<Layout gap="S">
|
||||
<Body size="S">Monthly</Body>
|
||||
<div class="usages">
|
||||
{#each monthlyUsage as usage}
|
||||
<div class="usage">
|
||||
<Usage {usage} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Layout>
|
||||
<div />
|
||||
{/if}
|
||||
</Layout>
|
||||
{/if}
|
||||
</Page>
|
||||
|
||||
<style>
|
||||
.usages {
|
||||
display: grid;
|
||||
column-gap: 60px;
|
||||
row-gap: 50px;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
</style>
|
|
@ -6,3 +6,4 @@ export { email } from "./email"
|
|||
export { auth } from "./auth"
|
||||
export { oidc } from "./oidc"
|
||||
export { templates } from "./templates"
|
||||
export { licensing } from "./licensing"
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import { writable } from "svelte/store"
|
||||
import { API } from "api"
|
||||
|
||||
export const createLicensingStore = () => {
|
||||
const DEFAULT = {
|
||||
plans: {},
|
||||
}
|
||||
|
||||
const store = writable(DEFAULT)
|
||||
|
||||
const actions = {
|
||||
getQuotaUsage: async () => {
|
||||
const quotaUsage = await API.getQuotaUsage()
|
||||
store.update(state => {
|
||||
return {
|
||||
...state,
|
||||
quotaUsage,
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
...actions,
|
||||
}
|
||||
}
|
||||
|
||||
export const licensing = createLicensingStore()
|
|
@ -27,4 +27,10 @@ export const buildLicensingEndpoints = API => ({
|
|||
url: "/api/global/license/refresh",
|
||||
})
|
||||
},
|
||||
|
||||
getQuotaUsage: async () => {
|
||||
return API.get({
|
||||
url: "/api/license/usage",
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue