mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
update: component to scroll to, better scroll logic.
This commit is contained in:
@@ -70,7 +70,8 @@
|
||||
class={resolvedClasses}
|
||||
style="pointer-events: {internalDisabled ? 'none' : 'auto'};"
|
||||
aria-label={ariaLabel}
|
||||
use:multiAction={actions}>
|
||||
use:multiAction={actions}
|
||||
{...$$restProps}>
|
||||
<slot />
|
||||
</a>
|
||||
{:else}
|
||||
@@ -81,7 +82,8 @@
|
||||
class={resolvedClasses}
|
||||
aria-label={ariaLabel}
|
||||
type={submit ? 'submit' : 'button'}
|
||||
use:multiAction={actions}>
|
||||
use:multiAction={actions}
|
||||
{...$$restProps}>
|
||||
{#if $isSubmitting && submissionLoader}
|
||||
<span
|
||||
class="loader is-small"
|
||||
|
||||
@@ -18,9 +18,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { currentPlan, organization } from '$lib/stores/organization';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { scrollToUpdateBudget } from '$routes/(console)/organization-[organization]/billing/store';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let alertsEnabled = false;
|
||||
|
||||
@@ -80,121 +78,106 @@
|
||||
|
||||
$: isButtonDisabled =
|
||||
symmetricDifference(alerts, $organization.budgetAlerts).length === 0 || !alertsEnabled;
|
||||
|
||||
let formHolderElement: HTMLDivElement;
|
||||
|
||||
$: if ($scrollToUpdateBudget) {
|
||||
history.replaceState(null, '', $page.url.pathname);
|
||||
|
||||
tick().then(() => {
|
||||
formHolderElement?.scrollIntoView({
|
||||
block: 'nearest'
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="update-budget-section" bind:this={formHolderElement} style:padding-block-start="1.5rem">
|
||||
<Form onSubmit={updateBudget}>
|
||||
<CardGrid>
|
||||
<Heading tag="h2" size="6">Billing alerts</Heading>
|
||||
<Form onSubmit={updateBudget}>
|
||||
<CardGrid>
|
||||
<Heading tag="h2" size="6">Billing alerts</Heading>
|
||||
|
||||
<p class="text">
|
||||
{#if !$currentPlan.budgeting}
|
||||
Get notified by email when your organization meets a percentage of your budget
|
||||
cap. <b
|
||||
>{tierToPlan($organization.billingPlan).name} organizations will receive one
|
||||
notification at 75% resource usage.</b>
|
||||
{:else}
|
||||
Get notified by email when your organization meets or exceeds a percentage of
|
||||
your specified billing alert(s).
|
||||
{/if}
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if !$currentPlan.budgeting}
|
||||
<p class="text">
|
||||
{#if !$currentPlan.budgeting}
|
||||
Get notified by email when your organization meets a percentage of your budget cap. <b
|
||||
>{tierToPlan($organization.billingPlan).name} organizations will receive one notification
|
||||
at 75% resource usage.</b>
|
||||
{:else}
|
||||
Get notified by email when your organization meets or exceeds a percentage of your
|
||||
specified billing alert(s).
|
||||
{/if}
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if !$currentPlan.budgeting}
|
||||
<Alert type="info">
|
||||
<svelte:fragment slot="title"
|
||||
>Billing alerts are a Pro plan feature
|
||||
</svelte:fragment>
|
||||
Upgrade to a Pro plan to manage when you receive billing alerts for your organization.
|
||||
</Alert>
|
||||
{:else}
|
||||
<FormList>
|
||||
<Alert type="info">
|
||||
<svelte:fragment slot="title"
|
||||
>Billing alerts are a Pro plan feature
|
||||
</svelte:fragment>
|
||||
Upgrade to a Pro plan to manage when you receive billing alerts for your organization.
|
||||
You can set a maximum of 4 billing alerts per organization.
|
||||
</Alert>
|
||||
{:else}
|
||||
<FormList>
|
||||
<Alert type="info">
|
||||
You can set a maximum of 4 billing alerts per organization.
|
||||
</Alert>
|
||||
|
||||
<div class="u-flex u-gap-16">
|
||||
<InputSelectSearch
|
||||
disabled={!alertsEnabled}
|
||||
label="Percentage (%) of budget cap"
|
||||
placeholder="Select a percentage"
|
||||
id="alerts"
|
||||
{options}
|
||||
bind:search
|
||||
interactiveOutput
|
||||
bind:value={selectedAlert}
|
||||
on:select={() => (search = selectedAlert.toString())} />
|
||||
<div style="align-self: flex-end">
|
||||
<Button
|
||||
secondary
|
||||
disabled={alerts.length > 3 ||
|
||||
(!search && !selectedAlert) ||
|
||||
!alertsEnabled}
|
||||
on:click={addAlert}>
|
||||
Add alert
|
||||
</Button>
|
||||
</div>
|
||||
<div class="u-flex u-gap-16">
|
||||
<InputSelectSearch
|
||||
disabled={!alertsEnabled}
|
||||
label="Percentage (%) of budget cap"
|
||||
placeholder="Select a percentage"
|
||||
id="alerts"
|
||||
{options}
|
||||
bind:search
|
||||
interactiveOutput
|
||||
bind:value={selectedAlert}
|
||||
on:select={() => (search = selectedAlert.toString())} />
|
||||
<div style="align-self: flex-end">
|
||||
<Button
|
||||
secondary
|
||||
disabled={alerts.length > 3 ||
|
||||
(!search && !selectedAlert) ||
|
||||
!alertsEnabled}
|
||||
on:click={addAlert}>
|
||||
Add alert
|
||||
</Button>
|
||||
</div>
|
||||
</FormList>
|
||||
</div>
|
||||
</FormList>
|
||||
|
||||
{#if alerts.length}
|
||||
<Table noMargin noStyles transparent>
|
||||
<TableHeader>
|
||||
<TableCellHead>Alert at budget cap %</TableCellHead>
|
||||
<TableCellHead width={30} />
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each alerts.sort() as alert}
|
||||
<TableRow>
|
||||
<TableCellText title="Percentage">
|
||||
{alert}%
|
||||
</TableCellText>
|
||||
<TableCell>
|
||||
<Button
|
||||
text
|
||||
round
|
||||
ariaLabel="remove alert"
|
||||
on:click={() =>
|
||||
(alerts = alerts.filter((a) => a !== alert))}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{/if}
|
||||
{#if alerts.length}
|
||||
<Table noMargin noStyles transparent>
|
||||
<TableHeader>
|
||||
<TableCellHead>Alert at budget cap %</TableCellHead>
|
||||
<TableCellHead width={30} />
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each alerts.sort() as alert}
|
||||
<TableRow>
|
||||
<TableCellText title="Percentage">
|
||||
{alert}%
|
||||
</TableCellText>
|
||||
<TableCell>
|
||||
<Button
|
||||
text
|
||||
round
|
||||
ariaLabel="remove alert"
|
||||
on:click={() =>
|
||||
(alerts = alerts.filter((a) => a !== alert))}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE || $organization?.billingPlan === BillingPlan.GITHUB_EDUCATION}
|
||||
<Button
|
||||
secondary
|
||||
href={$upgradeURL}
|
||||
on:click={() => {
|
||||
trackEvent('click_organization_upgrade', {
|
||||
from: 'button',
|
||||
source: 'billing_alerts_card'
|
||||
});
|
||||
}}
|
||||
>Upgrade to Pro
|
||||
</Button>
|
||||
{:else}
|
||||
<Button disabled={isButtonDisabled} submit>Update</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
</div>
|
||||
<svelte:fragment slot="actions">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE || $organization?.billingPlan === BillingPlan.GITHUB_EDUCATION}
|
||||
<Button
|
||||
secondary
|
||||
href={$upgradeURL}
|
||||
on:click={() => {
|
||||
trackEvent('click_organization_upgrade', {
|
||||
from: 'button',
|
||||
source: 'billing_alerts_card'
|
||||
});
|
||||
}}
|
||||
>Upgrade to Pro
|
||||
</Button>
|
||||
{:else}
|
||||
<Button disabled={isButtonDisabled} submit>Update</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { organization, currentPlan } from '$lib/stores/organization';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import BudgetAlert from './budgetAlert.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { scrollToUpdateBudget } from './store';
|
||||
|
||||
let capActive = false;
|
||||
let budget: number;
|
||||
@@ -47,72 +49,91 @@
|
||||
$: if (!capActive) {
|
||||
budget = null;
|
||||
}
|
||||
|
||||
let formHolderElement: HTMLDivElement;
|
||||
|
||||
$: if ($scrollToUpdateBudget) {
|
||||
// clean the url in the search bar.
|
||||
history.replaceState(null, '', $page.url.pathname);
|
||||
|
||||
tick().then(() => {
|
||||
const elementTop = formHolderElement.getBoundingClientRect().top + window.scrollY;
|
||||
// Offset 60 to prevent misalignment.
|
||||
window.scrollTo({ top: elementTop - 60 });
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form onSubmit={updateBudget}>
|
||||
<CardGrid>
|
||||
<Heading tag="h2" size="6">Budget cap</Heading>
|
||||
<div id="update-budget-section" bind:this={formHolderElement} style:padding-block-start="1.5rem">
|
||||
<Form onSubmit={updateBudget}>
|
||||
<CardGrid>
|
||||
<Heading tag="h2" size="6">Budget cap</Heading>
|
||||
|
||||
<p class="text">
|
||||
Restrict your resource usage by setting a budget cap. Cap usage is reset at the
|
||||
beginning of each billing cycle.
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if !$currentPlan.budgeting}
|
||||
<Alert type="info">
|
||||
<svelte:fragment slot="title">
|
||||
Budget caps are a Pro plan feature
|
||||
</svelte:fragment>
|
||||
Upgrade to a Pro plan to set a budget cap for your organization. For more information
|
||||
on what you can do with a Pro plan,
|
||||
<a
|
||||
class="link"
|
||||
href="https://appwrite.io/pricing"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">view our pricing guide.</a>
|
||||
</Alert>
|
||||
{:else}
|
||||
<FormList>
|
||||
<InputSwitch id="cap-active" label="Enable budget cap" bind:value={capActive}>
|
||||
<svelte:fragment slot="description">
|
||||
Budget cap limits do not include the base amount of your plan. <button
|
||||
class="link"
|
||||
type="button"
|
||||
on:click={() => ($showUsageRatesModal = true)}
|
||||
>Learn more about usage rates.
|
||||
</button>
|
||||
<p class="text">
|
||||
Restrict your resource usage by setting a budget cap. Cap usage is reset at the
|
||||
beginning of each billing cycle.
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if !$currentPlan.budgeting}
|
||||
<Alert type="info">
|
||||
<svelte:fragment slot="title">
|
||||
Budget caps are a Pro plan feature
|
||||
</svelte:fragment>
|
||||
</InputSwitch>
|
||||
{#if capActive}
|
||||
<InputNumber
|
||||
placeholder="Add budget cap"
|
||||
id="cap"
|
||||
autofocus
|
||||
label="Budget cap (USD)"
|
||||
bind:value={budget} />
|
||||
{/if}
|
||||
</FormList>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
Upgrade to a Pro plan to set a budget cap for your organization. For more information
|
||||
on what you can do with a Pro plan,
|
||||
<a
|
||||
class="link"
|
||||
href="https://appwrite.io/pricing"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">view our pricing guide.</a>
|
||||
</Alert>
|
||||
{:else}
|
||||
<FormList>
|
||||
<InputSwitch
|
||||
id="cap-active"
|
||||
label="Enable budget cap"
|
||||
bind:value={capActive}>
|
||||
<svelte:fragment slot="description">
|
||||
Budget cap limits do not include the base amount of your plan. <button
|
||||
class="link"
|
||||
type="button"
|
||||
on:click={() => ($showUsageRatesModal = true)}
|
||||
>Learn more about usage rates.
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
</InputSwitch>
|
||||
{#if capActive}
|
||||
<InputNumber
|
||||
placeholder="Add budget cap"
|
||||
id="cap"
|
||||
autofocus
|
||||
label="Budget cap (USD)"
|
||||
bind:value={budget} />
|
||||
{/if}
|
||||
</FormList>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
<Button
|
||||
secondary
|
||||
href={$upgradeURL}
|
||||
on:click={() => {
|
||||
trackEvent('click_organization_upgrade', {
|
||||
from: 'button',
|
||||
source: 'billing_budget_cap'
|
||||
});
|
||||
}}
|
||||
>Upgrade to Pro
|
||||
</Button>
|
||||
{:else}
|
||||
<Button disabled={$organization?.billingBudget === budget} submit>Update</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
<svelte:fragment slot="actions">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
<Button
|
||||
secondary
|
||||
href={$upgradeURL}
|
||||
on:click={() => {
|
||||
trackEvent('click_organization_upgrade', {
|
||||
from: 'button',
|
||||
source: 'billing_budget_cap'
|
||||
});
|
||||
}}
|
||||
>Upgrade to Pro
|
||||
</Button>
|
||||
{:else}
|
||||
<Button disabled={$organization?.billingBudget === budget} submit
|
||||
>Update</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<BudgetAlert alertsEnabled={capActive && budget > 0} />
|
||||
|
||||
@@ -6,18 +6,9 @@
|
||||
import { HeaderAlert } from '$lib/layout';
|
||||
import { hideBillingHeaderRoutes, readOnly, showBudgetAlert } from '$lib/stores/billing';
|
||||
import { base } from '$app/paths';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
$: isSameUrl = $page.url.pathname === redirectUrl;
|
||||
$: redirectUrl = `${base}/organization-${$organization.$id}/billing`;
|
||||
|
||||
function scrollOrNavigate() {
|
||||
if (isSameUrl) {
|
||||
document.querySelector('#update-budget-section')?.scrollIntoView({ block: 'nearest' });
|
||||
} else {
|
||||
goto(`${redirectUrl}#update-budget`);
|
||||
}
|
||||
}
|
||||
$: redirectUrl = `${base}/organization-${$organization.$id}/billing#update-budget`;
|
||||
$: isSameUrl = $page.url.pathname === new URL(redirectUrl, window.location.origin).pathname;
|
||||
</script>
|
||||
|
||||
{#if $showBudgetAlert && $organization?.$id && $organization?.billingPlan !== BillingPlan.FREE && $readOnly && !hideBillingHeaderRoutes.includes($page.url.pathname)}
|
||||
@@ -30,7 +21,9 @@
|
||||
<Button href={`${base}/organization-${$organization.$id}/usage`} text fullWidthMobile>
|
||||
<span class="text">View usage</span>
|
||||
</Button>
|
||||
<Button secondary fullWidthMobile on:click={scrollOrNavigate}>
|
||||
|
||||
<!-- target="_self" for same page navigation -->
|
||||
<Button secondary fullWidthMobile href={redirectUrl} target={isSameUrl ? '_self' : ''}>
|
||||
<span class="text">Update limit</span>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
|
||||
Reference in New Issue
Block a user