diff --git a/src/lib/components/billing/alerts/markedForDeletion.svelte b/src/lib/components/billing/alerts/markedForDeletion.svelte
new file mode 100644
index 000000000..e6323850b
--- /dev/null
+++ b/src/lib/components/billing/alerts/markedForDeletion.svelte
@@ -0,0 +1,14 @@
+
+
+{#if $organization?.markedForDeletion && !$page.url.pathname.includes('/console/account')}
+
+
+ All existing projects in the {$organization.name} organization have been paused. This organization
+ will be deleted once your upcoming invoice is processed successfully.
+
+
+{/if}
diff --git a/src/lib/components/billing/alerts/paymentAuthRequired.svelte b/src/lib/components/billing/alerts/paymentAuthRequired.svelte
new file mode 100644
index 000000000..257b86d16
--- /dev/null
+++ b/src/lib/components/billing/alerts/paymentAuthRequired.svelte
@@ -0,0 +1,29 @@
+
+
+{#if $actionRequiredInvoices && $actionRequiredInvoices.total && !$page.url.pathname.includes('/console/account')}
+
+ Please authorize your upcoming payment for {$organization.name}. Your bank requires this
+ security measure to proceed with payment.
+
+
+
+
+
+{/if}
diff --git a/src/lib/components/billing/alerts/projectsAtRisk.svelte b/src/lib/components/billing/alerts/projectsAtRisk.svelte
new file mode 100644
index 000000000..348fe3ebd
--- /dev/null
+++ b/src/lib/components/billing/alerts/projectsAtRisk.svelte
@@ -0,0 +1,32 @@
+
+
+{#if $failedInvoice && !$page.url.pathname.includes('/console/account')}
+ {@const daysPassed = diffDays(new Date($failedInvoice.dueAt), new Date())}
+
+
+ {#if daysPassed > 30}
+ Your scheduled payment on {toLocaleDate($failedInvoice?.dueAt)} failed. To resume
+ write access of your organization, please update your billing details.
+ {:else}
+ Your scheduled payment on {toLocaleDate($failedInvoice?.dueAt)} failed. Access
+ to paid projects within this organization will be disabled if no action is taken within
+ 30 days.
+ {/if}
+
+
+
+
+
+{/if}
diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts
index b2d22568c..09ec9294e 100644
--- a/src/lib/sdk/billing.ts
+++ b/src/lib/sdk/billing.ts
@@ -86,9 +86,9 @@ export type Credit = {
*/
credits: number;
/**
- * Used up credit amount
+ * Remaining up credit amount
*/
- creditsUsed: number;
+ creditsRemaining: number;
/**
* Credit expiration time in ISO 8601 format.
*/
diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts
index a07f8de1d..3dd4640e8 100644
--- a/src/lib/stores/billing.ts
+++ b/src/lib/stores/billing.ts
@@ -2,7 +2,7 @@ import { page } from '$app/stores';
import { derived, get, writable } from 'svelte/store';
import { sdk } from './sdk';
import { organization } from './organization';
-import type { AddressesList, Invoice, PaymentList, PlansInfo } from '$lib/sdk/billing';
+import type { InvoiceList, AddressesList, Invoice, PaymentList, PlansInfo } from '$lib/sdk/billing';
import { isCloud } from '$lib/system';
import { cachedStore } from '$lib/helpers/cache';
@@ -87,6 +87,8 @@ export const failedInvoice = cachedStore<
};
});
+export const actionRequiredInvoices = writable(null);
+
export type TierData = {
name: string;
description: string;
diff --git a/src/lib/stores/headerAlert.ts b/src/lib/stores/headerAlert.ts
new file mode 100644
index 000000000..9b9cae830
--- /dev/null
+++ b/src/lib/stores/headerAlert.ts
@@ -0,0 +1,49 @@
+import type { SvelteComponent } from 'svelte';
+import { writable } from 'svelte/store';
+
+export type HeaderAlert = {
+ show: boolean;
+ component: typeof SvelteComponent;
+ importance: number;
+};
+
+export type HeaderAlertStore = {
+ components: HeaderAlert[];
+};
+
+function createHeaderAlertStore() {
+ const { subscribe, update, set } = writable({
+ components: []
+ });
+
+ return {
+ subscribe,
+ set,
+ add: (component: HeaderAlert) => {
+ update((n) => {
+ n.components.push(component);
+ n.components = n.components;
+ return n;
+ });
+ },
+ get: (): HeaderAlert => {
+ //return higest importance component
+ let component = {
+ show: false,
+ component: null,
+ importance: 0
+ };
+ update((n) => {
+ n.components.forEach((c) => {
+ if (c.importance > component.importance) {
+ component = c;
+ }
+ });
+ return n;
+ });
+ return component;
+ }
+ };
+}
+
+export const headerAlert = createHeaderAlertStore();
diff --git a/src/routes/console/+layout.svelte b/src/routes/console/+layout.svelte
index 813f78833..38d186109 100644
--- a/src/routes/console/+layout.svelte
+++ b/src/routes/console/+layout.svelte
@@ -14,11 +14,11 @@
import { loading, requestedMigration } from '../store';
import Create from './createOrganization.svelte';
import {
- failedInvoice,
daysLeftInTrial,
tierToPlan,
readOnly,
- showUsageRatesModal
+ showUsageRatesModal,
+ actionRequiredInvoices
} from '$lib/stores/billing';
import { diffDays, toLocaleDate } from '$lib/helpers/date';
import { base } from '$app/paths';
@@ -40,9 +40,12 @@
import ExcesLimitModal from './organization-[organization]/excesLimitModal.svelte';
import { showExcess } from './organization-[organization]/store';
import UsageRates from './wizard/cloudOrganization/usageRates.svelte';
- import { Button } from '$lib/elements/forms';
import { consoleVariables, showPrereleaseModal } from './store';
import PreReleaseModal from './(billing-modal)/preReleaseModal.svelte';
+ import { Query } from '@appwrite.io/console';
+ import { headerAlert } from '$lib/stores/headerAlert';
+ import MarkedForDeletion from '$lib/components/billing/alerts/markedForDeletion.svelte';
+ import PaymentAuthRequired from '$lib/components/billing/alerts/paymentAuthRequired.svelte';
function kebabToSentenceCase(str: string) {
return str
@@ -290,6 +293,14 @@
checkForTrialEnding();
paymentExpired();
checkForUsageLimit();
+ if ($organization?.markedForDeletion) {
+ headerAlert.add({
+ component: MarkedForDeletion,
+ show: true,
+ importance: 5
+ });
+ }
+ checkPaymentAuthorizationRequired();
}
});
@@ -419,6 +430,20 @@
}
}
+ async function checkPaymentAuthorizationRequired() {
+ if ($organization.billingPlan === 'tier-0') return;
+ $actionRequiredInvoices = await sdk.forConsole.billing.listInvoices($organization.$id, [
+ Query.equal('status', 'requires_action')
+ ]);
+ if ($actionRequiredInvoices && $actionRequiredInvoices.total) {
+ headerAlert.add({
+ component: PaymentAuthRequired,
+ show: true,
+ importance: 6
+ });
+ }
+ }
+
$: if (!$log.show) {
$log.data = null;
$log.func = null;
@@ -429,6 +454,9 @@
}
$registerSearchers(orgSearcher, projectsSearcher);
+
+ $: selectedHeaderAlert = headerAlert.get();
+ $: console.log(selectedHeaderAlert);
@@ -441,29 +469,10 @@
!$page.url.pathname.includes('/console/card') &&
!$page.url.pathname.includes('/console/onboarding')}>
- {#if $failedInvoice && !$page.url.pathname.includes('/console/account')}
- {@const daysPassed = diffDays(new Date($failedInvoice.dueAt), new Date())}
-
-
- {#if daysPassed > 30}
- Your scheduled payment on {toLocaleDate($failedInvoice?.dueAt)} failed.
- To resume write access of your organization, please update your billing details.
- {:else}
- Your scheduled payment on {toLocaleDate($failedInvoice?.dueAt)} failed.
- Access to paid projects within this organization will be disabled if no action
- is taken within 30 days.
- {/if}
-
-
-
-
-
+ {#if selectedHeaderAlert?.show}
+
{/if}
+
{#if $organization?.markedForDeletion && !$page.url.pathname.includes('/console/account')}
diff --git a/src/routes/console/organization-[organization]/+layout.ts b/src/routes/console/organization-[organization]/+layout.ts
index 0548d6cca..c030fa03c 100644
--- a/src/routes/console/organization-[organization]/+layout.ts
+++ b/src/routes/console/organization-[organization]/+layout.ts
@@ -6,6 +6,9 @@ import { error } from '@sveltejs/kit';
import type { LayoutLoad } from './$types';
import Breadcrumbs from './breadcrumbs.svelte';
import Header from './header.svelte';
+import { headerAlert } from '$lib/stores/headerAlert';
+import ProjectsAtRisk from '$lib/components/billing/alerts/projectsAtRisk.svelte';
+import { get } from 'svelte/store';
export const load: LayoutLoad = async ({ params, depends }) => {
depends(Dependencies.ORGANIZATION);
@@ -13,6 +16,14 @@ export const load: LayoutLoad = async ({ params, depends }) => {
if (isCloud) {
await failedInvoice.load(params.organization);
+
+ if (!get(failedInvoice)) {
+ headerAlert.add({
+ show: true,
+ component: ProjectsAtRisk,
+ importance: 1
+ });
+ }
}
try {
diff --git a/src/routes/console/organization-[organization]/billing/availableCredit.svelte b/src/routes/console/organization-[organization]/billing/availableCredit.svelte
index 602d9eab0..8006745ce 100644
--- a/src/routes/console/organization-[organization]/billing/availableCredit.svelte
+++ b/src/routes/console/organization-[organization]/billing/availableCredit.svelte
@@ -52,7 +52,10 @@
request();
}
- $: balance = creditList?.credits?.reduce((acc: number, curr: Credit) => acc + curr.credits, 0);
+ $: balance = creditList?.credits?.reduce(
+ (acc: number, curr: Credit) => acc + curr.creditsRemaining,
+ 0
+ );
@@ -65,7 +68,7 @@
Credit balance ${balance}
{#if creditList?.total}
-