Skip to content

Commit

Permalink
Merge pull request #1331 from appwrite/poc-invoice-cycle-ref
Browse files Browse the repository at this point in the history
Feat: invoice cycle changes
  • Loading branch information
lohanidamodar authored Feb 14, 2025
2 parents 1f1e845 + cfe49e0 commit 0afd6d2
Show file tree
Hide file tree
Showing 34 changed files with 1,091 additions and 488 deletions.
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const config: PlaywrightTestConfig = {
webServer: {
timeout: 120000,
env: {
PUBLIC_APPWRITE_ENDPOINT: 'https://console-testing-2.appwrite.org/v1',
PUBLIC_APPWRITE_ENDPOINT: 'https://dlbillingic.appwrite.org/v1',
PUBLIC_CONSOLE_MODE: 'cloud',
PUBLIC_STRIPE_KEY:
'pk_test_51LT5nsGYD1ySxNCyd7b304wPD8Y1XKKWR6hqo6cu3GIRwgvcVNzoZv4vKt5DfYXL1gRGw4JOqE19afwkJYJq1g3K004eVfpdWn'
Expand Down
2 changes: 1 addition & 1 deletion src/lib/commandCenter/searchers/organizations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { sdk } from '$lib/stores/sdk';
import type { Searcher } from '../commands';

export const orgSearcher = (async (query: string) => {
const { teams } = await sdk.forConsole.teams.list();
const { teams } = await sdk.forConsole.billing.listOrganization();
return teams
.filter((organization) => organization.name.toLowerCase().includes(query.toLowerCase()))
.map((organization) => {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/billing/alerts/newDevUpgradePro.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { base } from '$app/paths';
import { page } from '$app/stores';
import { trackEvent } from '$lib/actions/analytics';
import { BillingPlan } from '$lib/constants';
import { BillingPlan, NEW_DEV_PRO_UPGRADE_COUPON } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { organization } from '$lib/stores/organization';
import { activeHeaderAlert } from '$routes/(console)/store';
Expand All @@ -29,7 +29,7 @@
secondary
fullWidthMobile
class="u-line-height-1"
href={`${base}/apply-credit?code=appw50&org=${$organization.$id}`}
href={`${base}/apply-credit?code=${NEW_DEV_PRO_UPGRADE_COUPON}&org=${$organization.$id}`}
on:click={() => {
trackEvent('click_credits_redeem', {
from: 'button',
Expand Down
47 changes: 47 additions & 0 deletions src/lib/components/billing/discountsApplied.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script lang="ts">
import { formatCurrency } from '$lib/helpers/numbers';
import type { Coupon } from '$lib/sdk/billing';
export let label: string;
export let value: number;
export let couponData: Partial<Coupon> = {
code: null,
status: null,
credits: null
};
export let fixedCoupon = false;
</script>

{#if value > 0}
<span class="u-flex u-main-space-between">
<div class="u-flex u-cross-center u-gap-4">
<p class="text">
<span class="icon-tag u-color-text-success" aria-hidden="true" />
<span>
{label}
</span>
</p>
{#if !fixedCoupon && label.toLowerCase() === 'credits'}
<button
type="button"
class="button is-text is-only-icon"
style="--button-size:1.5rem;"
aria-label="Close"
title="Close"
on:click={() =>
(couponData = {
code: null,
status: null,
credits: null
})}>
<span class="icon-x" aria-hidden="true" />
</button>
{/if}
</div>
{#if value >= 100}
<p class="inline-tag">Credits applied</p>
{:else}
<span class="u-color-text-success">-{formatCurrency(value)}</span>
{/if}
</span>
{/if}
146 changes: 146 additions & 0 deletions src/lib/components/billing/estimatedTotal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<script lang="ts">
import { FormList, InputChoice, InputNumber } from '$lib/elements/forms';
import { formatCurrency } from '$lib/helpers/numbers';
import type { Coupon, Estimation } from '$lib/sdk/billing';
import { sdk } from '$lib/stores/sdk';
import { AppwriteException } from '@appwrite.io/console';
import Card from '../card.svelte';
import DiscountsApplied from './discountsApplied.svelte';
import { addNotification } from '$lib/stores/notifications';
export let organizationId: string | undefined = undefined;
export let billingPlan: string;
export let collaborators: string[];
export let fixedCoupon = false;
export let couponData: Partial<Coupon>;
export let billingBudget: number;
let budgetEnabled = false;
let estimation: Estimation;
async function getEstimate(
billingPlan: string,
collaborators: string[],
couponId: string | undefined
) {
try {
estimation = await sdk.forConsole.billing.estimationCreateOrganization(
billingPlan,
couponId === '' ? null : couponId,
collaborators ?? []
);
} catch (e) {
if (e instanceof AppwriteException) {
if (
e.type === 'billing_coupon_not_found' ||
e.type === 'billing_coupon_already_used' ||
e.type === 'billing_credit_unsupported'
) {
couponData = {
code: null,
status: null,
credits: null
};
}
}
addNotification({
type: 'error',
isHtml: false,
message: e.message
});
}
}
async function getUpdatePlanEstimate(
organizationId: string,
billingPlan: string,
collaborators: string[],
couponId: string | undefined
) {
try {
estimation = await sdk.forConsole.billing.estimationUpdatePlan(
organizationId,
billingPlan,
couponId && couponId.length > 0 ? couponId : null,
collaborators ?? []
);
} catch (e) {
if (e instanceof AppwriteException) {
if (
e.type === 'billing_coupon_not_found' ||
e.type === 'billing_coupon_already_used' ||
e.type === 'billing_credit_unsupported'
) {
couponData = {
code: null,
status: null,
credits: null
};
}
}
addNotification({
type: 'error',
isHtml: false,
message: e.message
});
}
}
$: organizationId
? getUpdatePlanEstimate(organizationId, billingPlan, collaborators, couponData?.code)
: getEstimate(billingPlan, collaborators, couponData?.code);
</script>

{#if estimation}
<Card class="u-flex u-flex-vertical u-gap-8">
<slot />
{#if estimation}
{#each estimation.items ?? [] as item}
{#if item.value > 0}
<span class="u-flex u-main-space-between">
<p class="text">{item.label}</p>
<p class="text">{formatCurrency(item.value)}</p>
</span>
{/if}
{/each}
{#each estimation.discounts ?? [] as item}
<DiscountsApplied {fixedCoupon} bind:couponData {...item} />
{/each}
<div class="u-sep-block-start" />
<span class="u-flex u-main-space-between">
<p class="text">Total due</p>
<p class="text">
{formatCurrency(estimation.grossAmount)}
</p>
</span>

<p class="text u-margin-block-start-16">
You'll pay <span class="u-bold">{formatCurrency(estimation.grossAmount)}</span> now.
{#if couponData?.code}Once your credits run out,{:else}Then{/if} you'll be charged
<span class="u-bold">{formatCurrency(estimation.amount)}</span> every 30 days.
</p>
{/if}

<FormList class="u-margin-block-start-24">
<InputChoice
type="switchbox"
id="budget"
label="Enable budget cap"
tooltip="If enabled, you will be notified when your spending reaches 75% of the set cap. Update cap alerts in your organization settings."
fullWidth
bind:value={budgetEnabled}>
{#if budgetEnabled}
<div class="u-margin-block-start-16">
<InputNumber
id="budget"
label="Budget cap (USD)"
placeholder="0"
min={0}
bind:value={billingBudget} />
</div>
{/if}
</InputChoice>
</FormList>
</Card>
{/if}
99 changes: 0 additions & 99 deletions src/lib/components/billing/estimatedTotalBox.svelte

This file was deleted.

3 changes: 2 additions & 1 deletion src/lib/components/billing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ export { default as PaymentBoxes } from './paymentBoxes.svelte';
export { default as CouponInput } from './couponInput.svelte';
export { default as SelectPaymentMethod } from './selectPaymentMethod.svelte';
export { default as UsageRates } from './usageRates.svelte';
export { default as EstimatedTotalBox } from './estimatedTotalBox.svelte';
export { default as PlanComparisonBox } from './planComparisonBox.svelte';
export { default as EmptyCardCloud } from './emptyCardCloud.svelte';
export { default as CreditsApplied } from './creditsApplied.svelte';
export { default as PlanSelection } from './planSelection.svelte';
export { default as EstimatedTotal } from './estimatedTotal.svelte';
export { default as SelectPlan } from './selectPlan.svelte';
Loading

0 comments on commit 0afd6d2

Please sign in to comment.