diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts
index 6880f1c2f..dea8860b5 100644
--- a/src/lib/actions/analytics.ts
+++ b/src/lib/actions/analytics.ts
@@ -268,7 +268,7 @@ export enum Submit {
AuthSessionAlertsUpdate = 'submit_auth_session_alerts_update',
AuthMembershipPrivacyUpdate = 'submit_auth_membership_privacy_update',
AuthMockNumbersUpdate = 'submit_auth_mock_numbers_update',
- AuthInvalidateSesssion = 'submit_auth_invalidate_session',
+ AuthInvalidateSession = 'submit_auth_invalidate_session',
SessionsLengthUpdate = 'submit_sessions_length_update',
SessionsLimitUpdate = 'submit_sessions_limit_update',
SessionDelete = 'submit_session_delete',
@@ -277,6 +277,7 @@ export enum Submit {
DatabaseDelete = 'submit_database_delete',
DatabaseUpdateName = 'submit_database_update_name',
DatabaseImportCsv = 'submit_database_import_csv',
+ DatabaseBackupDelete = 'submit_database_backup_delete',
ColumnCreate = 'submit_column_create',
ColumnUpdate = 'submit_column_update',
diff --git a/src/lib/components/archiveProject.svelte b/src/lib/components/archiveProject.svelte
index 8a9797ca0..8eb379591 100644
--- a/src/lib/components/archiveProject.svelte
+++ b/src/lib/components/archiveProject.svelte
@@ -1,4 +1,5 @@
@@ -196,7 +207,7 @@
+ badge={`${archivedTotalOverall}`}>
{#if isPlanBelowPro}
These projects are archived and require a plan upgrade to restore access.
@@ -206,7 +217,7 @@
-
+
{#each projectsToArchive as project}
{@const platforms = filterPlatforms(
project.platforms.map((platform) => getPlatformInfo(platform.type))
@@ -266,7 +277,7 @@
{/each}
- {#if platforms.length > 3}
+ {#if platforms.length > 2}
{/each}
+
+
@@ -355,4 +375,7 @@
align-items: center;
gap: 8px;
}
+ :global(.pagination-container) {
+ margin-top: 16px;
+ }
diff --git a/src/lib/components/billing/alerts/paymentMandate.svelte b/src/lib/components/billing/alerts/paymentMandate.svelte
deleted file mode 100644
index 08d9207d7..000000000
--- a/src/lib/components/billing/alerts/paymentMandate.svelte
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-{#if $paymentMissingMandate && $paymentMissingMandate?.country?.toLowerCase() === 'in' && $paymentMissingMandate.mandateId === null && !hideBillingHeaderRoutes.includes(page.url.pathname)}
-
- The payment method for {$organization.name} needs to be verified.
-
- Verify payment method
-
-
-{/if}
diff --git a/src/lib/components/domains/cnameTable.svelte b/src/lib/components/domains/cnameTable.svelte
index b198524da..9569ce89f 100644
--- a/src/lib/components/domains/cnameTable.svelte
+++ b/src/lib/components/domains/cnameTable.svelte
@@ -35,16 +35,22 @@
-
+
- Type
- Name
- Value
+ Type
+ Name
+ Value
- CNAME
- {subdomain}
-
+ CNAME
+ {subdomain}
+
-
+
- Type
- Name
- Value
+ Type
+ Name
+ Value
- {variant.toUpperCase()}
- {subdomain || '@'}
-
+ {variant.toUpperCase()}
+ {subdomain || '@'}
+
{#if $regionalConsoleVariables._APP_DOMAIN_TARGET_CAA}
-
+
CAA
- @
-
+ @
+
diff --git a/src/lib/components/limit.svelte b/src/lib/components/limit.svelte
index c8e72a401..4221f7693 100644
--- a/src/lib/components/limit.svelte
+++ b/src/lib/components/limit.svelte
@@ -8,6 +8,8 @@
export let sum: number;
export let limit: number;
export let name: string;
+ export let pageParam: string = 'page';
+ export let removeOnFirstPage: boolean = false;
const options = [
{ label: '6', value: 6 },
@@ -23,10 +25,17 @@
url.searchParams.set('limit', limit.toString());
await preferences.setLimit(limit);
- if (url.searchParams.has('page')) {
- const page = Number(url.searchParams.get('page'));
- const newPage = Math.floor(((page - 1) * previousLimit) / limit);
- url.searchParams.set('page', newPage.toString());
+ if (url.searchParams.has(pageParam)) {
+ const page = Number(url.searchParams.get(pageParam));
+ const prev =
+ Number.isFinite(previousLimit) && previousLimit > 0 ? previousLimit : limit;
+ const newPage = Math.floor(((page - 1) * prev) / limit) + 1;
+ const safePage = Math.max(1, Number.isFinite(newPage) ? newPage : 1);
+ if (removeOnFirstPage && safePage === 1) {
+ url.searchParams.delete(pageParam);
+ } else {
+ url.searchParams.set(pageParam, safePage.toString());
+ }
}
await goto(url.toString());
diff --git a/src/lib/components/multiSelectTable.svelte b/src/lib/components/multiSelectTable.svelte
index cd35b38e6..ce0196e34 100644
--- a/src/lib/components/multiSelectTable.svelte
+++ b/src/lib/components/multiSelectTable.svelte
@@ -1,5 +1,13 @@
{#key computeKey}
@@ -104,13 +186,7 @@
if (confirmDeletion) {
showConfirmDeletion = true;
} else {
- const state = await onDelete?.(selectedRows);
- if (state instanceof Error) {
- // user should handle error on their own!
- } else {
- notifySuccess();
- selectedRows = [];
- }
+ await consumeDeleteOperation();
}
}}>Delete
@@ -129,16 +205,13 @@
disableModal = true;
onDeleteError = null;
- const state = await onDelete?.(selectedRows);
- if (state instanceof Error) {
- disableModal = false;
- onDeleteError = state.message || `Failed to delete ${resource}s`;
- } else {
- notifySuccess();
- selectedRows = [];
- disableModal = false;
+ const allDeleted = await consumeDeleteOperation();
+
+ if (allDeleted) {
showConfirmDeletion = false;
}
+
+ disableModal = false;
}}>
{@const selectionCount = selectedRows.length}
diff --git a/src/lib/components/pagination.svelte b/src/lib/components/pagination.svelte
index f272f7f66..975e4cf6e 100644
--- a/src/lib/components/pagination.svelte
+++ b/src/lib/components/pagination.svelte
@@ -6,15 +6,21 @@
export let limit: number;
export let offset: number;
export let useCreateLink = true;
+ export let pageParam: string = 'page';
+ export let removeOnFirstPage: boolean = false;
$: currentPage = Math.floor(offset / limit + 1);
function getLink(page: number): string {
const url = new URL(pageStore.url);
if (page === 1) {
- url.searchParams.delete('page');
+ if (removeOnFirstPage) {
+ url.searchParams.delete(pageParam);
+ } else {
+ url.searchParams.set(pageParam, '1');
+ }
} else {
- url.searchParams.set('page', page.toString());
+ url.searchParams.set(pageParam, page.toString());
}
return url.toString();
diff --git a/src/lib/components/paginationWithLimit.svelte b/src/lib/components/paginationWithLimit.svelte
index 7cf78f6b5..b48fbc0f6 100644
--- a/src/lib/components/paginationWithLimit.svelte
+++ b/src/lib/components/paginationWithLimit.svelte
@@ -8,13 +8,19 @@
offset,
total,
name,
- useCreateLink = true
+ useCreateLink = true,
+ pageParam = 'page',
+ removeOnFirstPage = false,
+ ...restProps
}: {
limit: number;
offset: number;
total: number;
name: string;
useCreateLink?: boolean;
+ pageParam?: string;
+ removeOnFirstPage?: boolean;
+ [key: string]: unknown;
} = $props();
const showLimit = $derived(!!useCreateLink);
@@ -22,10 +28,17 @@
const alignItems = $derived(showLimit ? 'center' : 'flex-end');
-
+
{#if showLimit}
-
+
{/if}
-
+
diff --git a/src/lib/components/paginator.svelte b/src/lib/components/paginator.svelte
index ae8aaf9c4..053e2ea14 100644
--- a/src/lib/components/paginator.svelte
+++ b/src/lib/components/paginator.svelte
@@ -13,7 +13,8 @@
name = 'items',
gap = 's',
offset = $bindable(0),
- children
+ children,
+ ...restProps
}: {
items: T[];
limit?: number;
@@ -26,6 +27,7 @@
| undefined;
offset?: number;
children: Snippet<[T[], number]>;
+ [key: string]: unknown;
} = $props();
let total = $derived(items.length);
@@ -33,7 +35,7 @@
let paginatedItems = $derived(items.slice(offset, offset + limit));
-
+
{@render children(paginatedItems, limit)}
{#if !hideFooter}
diff --git a/src/lib/helpers/date.ts b/src/lib/helpers/date.ts
index 2d56c1cb6..29878256a 100644
--- a/src/lib/helpers/date.ts
+++ b/src/lib/helpers/date.ts
@@ -206,6 +206,18 @@ export function timeFromNow(datetime: string): string {
return dayjs().to(dayjs(datetime));
}
+export function timeFromNowShort(datetime: string): string {
+ if (!datetime || !isValidDate(datetime)) {
+ return 'n/a';
+ }
+
+ const timeStr = dayjs().to(dayjs(datetime));
+ return timeStr
+ .replace('second', 'sec') // seconds > secs
+ .replace('minute', 'min') // minutes > mins
+ .replace('hour', 'hr'); // hours > hrs
+}
+
export function hoursToDays(hours: number) {
if (hours > 24) {
return `${Math.floor(hours / 24)} days`;
diff --git a/src/lib/layout/activity.svelte b/src/lib/layout/activity.svelte
index dce36d66f..c77fa3488 100644
--- a/src/lib/layout/activity.svelte
+++ b/src/lib/layout/activity.svelte
@@ -1,9 +1,9 @@
@@ -29,55 +49,57 @@
expanded={databasesScreen && !insideSideSheet}
slotSpacing={databasesScreen && !insideSideSheet}>
{#if logs.total}
-
-
-
- User
- Event
- Location
- IP
- Date
-
- {#each logs.logs as log}
-
-
-
- {#if log.userEmail}
- {#if log.userName}
-
- {log.userName}
- {:else}
-
- {log.userEmail}
- {/if}
+
+
+ User
+ Event
+ Location
+ IP
+ Date
+
+ {#each logs.logs as log}
+
+
+
+ {#if log.userEmail}
+ {#if log.userName}
+
+ {log.userName}
{:else}
-
-
-
- {log.userName ?? 'Anonymous'}
+
+ {log.userEmail}
{/if}
-
-
-
- {log.event}
-
-
- {#if log.countryCode !== '--'}
- {log.countryName}
{:else}
- Unknown
+
+
+
+ {log.userName ?? 'Anonymous'}
{/if}
-
-
- {log.ip}
-
-
- {toLocaleDateTime(log.time)}
-
-
- {/each}
-
-
+
+
+
+
+ {log.event}
+
+
+
+ {#if log.countryCode !== '--'}
+ {log.countryName}
+ {:else}
+ Unknown
+ {/if}
+
+
+
+
+
+
+
+
+
+
+ {/each}
+
{
- const path = `/account/payment-methods/${paymentMethodId}/setup`;
- const params = {
- organizationId,
- paymentMethodId
- };
- const uri = new URL(this.client.config.endpoint + path);
- return await this.client.call(
- 'patch',
- uri,
- {
- 'content-type': 'application/json'
- },
- params
- );
- }
-
async listAddresses(queries: string[] = []): Promise {
const path = `/account/billing-addresses`;
const params = {
diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts
index 695ef874c..df2d29178 100644
--- a/src/lib/stores/billing.ts
+++ b/src/lib/stores/billing.ts
@@ -8,7 +8,7 @@ import MarkedForDeletion from '$lib/components/billing/alerts/markedForDeletion.
import MissingPaymentMethod from '$lib/components/billing/alerts/missingPaymentMethod.svelte';
import newDevUpgradePro from '$lib/components/billing/alerts/newDevUpgradePro.svelte';
import PaymentAuthRequired from '$lib/components/billing/alerts/paymentAuthRequired.svelte';
-import PaymentMandate from '$lib/components/billing/alerts/paymentMandate.svelte';
+
import { BillingPlan, NEW_DEV_PRO_UPGRADE_COUPON } from '$lib/constants';
import { cachedStore } from '$lib/helpers/cache';
import { type Size, sizeToBytes } from '$lib/helpers/sizeConvertion';
@@ -18,7 +18,6 @@ import type {
Invoice,
InvoiceList,
PaymentList,
- PaymentMethodData,
Plan,
PlansMap
} from '$lib/sdk/billing';
@@ -535,24 +534,6 @@ export function checkForMarkedForDeletion(org: Organization) {
}
}
-export const paymentMissingMandate = writable(null);
-
-export async function checkForMandate(org: Organization) {
- const paymentId = org.paymentMethodId ?? org.backupPaymentMethodId;
- if (!paymentId) return;
- const paymentMethod = await sdk.forConsole.billing.getPaymentMethod(paymentId);
- if (paymentMethod?.mandateId === null && paymentMethod?.country.toLowerCase() === 'in') {
- headerAlert.add({
- id: 'paymentMandate',
- component: PaymentMandate,
- show: true,
- importance: 8
- });
- activeHeaderAlert.set(headerAlert.get());
- paymentMissingMandate.set(paymentMethod);
- }
-}
-
export async function checkForMissingPaymentMethod() {
const orgs = await sdk.forConsole.billing.listOrganization([
Query.notEqual('billingPlan', BillingPlan.FREE),
diff --git a/src/routes/(console)/+layout.svelte b/src/routes/(console)/+layout.svelte
index 109bdabb3..ce28dd7c2 100644
--- a/src/routes/(console)/+layout.svelte
+++ b/src/routes/(console)/+layout.svelte
@@ -15,7 +15,6 @@
import {
calculateTrialDay,
checkForEnterpriseTrial,
- checkForMandate,
checkForMarkedForDeletion,
checkForMissingPaymentMethod,
checkForNewDevUpgradePro,
@@ -308,7 +307,6 @@
if (org?.billingPlan !== BillingPlan.FREE) {
await paymentExpired(org);
await checkPaymentAuthorizationRequired(org);
- await checkForMandate(org);
if ($plansInfo.get(org.billingPlan)?.trialDays) {
calculateTrialDay(org);
@@ -334,7 +332,6 @@
- {toLocaleDateTime(identity.providerAccessTokenExpiry)}
+ {#if identity.providerAccessTokenExpiry}
+
+ {:else}
+ -
+ {/if}
deleteIdentity(identity.$id)}>
diff --git a/src/routes/(console)/account/payments/editPaymentModal.svelte b/src/routes/(console)/account/payments/editPaymentModal.svelte
index 7850cdea9..24a8fa08f 100644
--- a/src/routes/(console)/account/payments/editPaymentModal.svelte
+++ b/src/routes/(console)/account/payments/editPaymentModal.svelte
@@ -9,13 +9,27 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Alert } from '@appwrite.io/pink-svelte';
- export let show = false;
- export let selectedPaymentMethod: PaymentMethodData;
- export let isLinked = false;
+ let {
+ show = $bindable(false),
+ isLinked = false,
+ selectedPaymentMethod
+ }: {
+ show: boolean;
+ isLinked?: boolean;
+ selectedPaymentMethod: PaymentMethodData;
+ } = $props();
+
+ let year: number | null = $state(null);
+ let month: string | null = $state(null);
+ let error: string | null = $state(null);
+
const currentYear = new Date().getFullYear();
- let error: string;
- let month: string;
- let year: number;
+ const months = Array.from({ length: 12 }, (_, i) => {
+ const value = String(i + 1).padStart(2, '0');
+ return { value, label: value };
+ });
+
+ const options = $derived(createMonthOptions(year));
async function handleSubmit() {
try {
@@ -24,9 +38,9 @@
month,
year?.toString()
);
- trackEvent(Submit.PaymentMethodUpdate);
- invalidate(Dependencies.PAYMENT_METHODS);
show = false;
+ trackEvent(Submit.PaymentMethodUpdate);
+ await invalidate(Dependencies.PAYMENT_METHODS);
addNotification({
type: 'success',
message: 'Your payment method has been updated'
@@ -38,20 +52,6 @@
}
function createMonthOptions(year: number) {
- const months = [
- { value: '01', label: '01' },
- { value: '02', label: '02' },
- { value: '03', label: '03' },
- { value: '04', label: '04' },
- { value: '05', label: '05' },
- { value: '06', label: '06' },
- { value: '07', label: '07' },
- { value: '08', label: '08' },
- { value: '09', label: '09' },
- { value: '10', label: '10' },
- { value: '11', label: '11' },
- { value: '12', label: '12' }
- ];
if (!year) return months;
if (year === currentYear) {
const currentMonth = new Date().getMonth() + 1;
@@ -60,8 +60,6 @@
return months;
}
}
-
- $: options = createMonthOptions(year);
diff --git a/src/routes/(console)/account/sessions/+page.svelte b/src/routes/(console)/account/sessions/+page.svelte
index eb0c51de4..bbc261abc 100644
--- a/src/routes/(console)/account/sessions/+page.svelte
+++ b/src/routes/(console)/account/sessions/+page.svelte
@@ -13,7 +13,14 @@
import type { PageData } from './$types';
import { addNotification } from '$lib/stores/notifications';
import { onMount } from 'svelte';
- import { Badge, Layout, Table, Typography, Icon } from '@appwrite.io/pink-svelte';
+ import {
+ Badge,
+ Layout,
+ Table,
+ Typography,
+ Icon,
+ InteractiveText
+ } from '@appwrite.io/pink-svelte';
import { IconGlobeAlt } from '@appwrite.io/pink-icons-svelte';
export let data: PageData;
@@ -92,9 +99,9 @@
@@ -146,7 +153,7 @@
{/if}
- {session.ip}
+
logout(session)}>Sign out
diff --git a/src/routes/(console)/organization-[organization]/+layout.ts b/src/routes/(console)/organization-[organization]/+layout.ts
index ef77274e8..aa1b03b8d 100644
--- a/src/routes/(console)/organization-[organization]/+layout.ts
+++ b/src/routes/(console)/organization-[organization]/+layout.ts
@@ -24,7 +24,7 @@ export const load: LayoutLoad = async ({ params, depends, parent }) => {
depends(Dependencies.MEMBERS);
depends(Dependencies.PAYMENT_METHODS);
- const requestedOrg = await checkPlatformAndRedirect(params, organizations, prefs);
+ await checkPlatformAndRedirect(params, organizations, prefs);
let roles = isCloud ? [] : defaultRoles;
let scopes = isCloud ? [] : defaultScopes;
@@ -46,13 +46,8 @@ export const load: LayoutLoad = async ({ params, depends, parent }) => {
sdk.forConsole.account.updatePrefs({ prefs: newPrefs });
}
- // fetch org only if we haven't already fetched it for platform check
- const orgPromise: Promise = requestedOrg
- ? Promise.resolve(requestedOrg)
- : (sdk.forConsole.teams.get({ teamId: params.organization }) as Promise);
-
const [org, members, countryList, locale] = await Promise.all([
- orgPromise,
+ sdk.forConsole.teams.get({ teamId: params.organization }) as Promise,
sdk.forConsole.teams.listMemberships({ teamId: params.organization }),
sdk.forConsole.locale.listCountries(),
sdk.forConsole.locale.get(),
@@ -96,7 +91,7 @@ async function checkPlatformAndRedirect(
params: { organization: string },
organizations: { teams: Array<{ $id: string; platform?: string }> },
prefs: Record
-): Promise {
+) {
// check if preloaded
let requestedOrg = organizations.teams.find((team) => team.$id === params.organization) as
| Organization
@@ -154,8 +149,4 @@ async function checkPlatformAndRedirect(
redirect(303, resolve('/(console)'));
}
}
-
- // send the org,
- // if already in the full list so we don't have to make another API request.
- return requestedOrg;
}
diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte
index 4794d8659..83c40623c 100644
--- a/src/routes/(console)/organization-[organization]/+page.svelte
+++ b/src/routes/(console)/organization-[organization]/+page.svelte
@@ -71,12 +71,6 @@
return getServiceLimit('projects', null, data.currentPlan);
});
- const projectsToArchive = $derived.by(() => {
- return isCloud
- ? data.projects.projects.filter((project) => project.status === 'archived')
- : [];
- });
-
function filterPlatforms(platforms: { name: string; icon: string }[]) {
return platforms.filter(
(value, index, self) => index === self.findIndex((t) => t.name === value.name)
@@ -136,6 +130,19 @@
return project.status === 'archived';
}
+ const projectsToArchive = $derived(
+ (data.archivedProjectsPage ?? data.projects.projects).filter(
+ (project) => project.status === 'archived'
+ )
+ );
+
+ const activeTotalOverall = $derived(
+ data?.activeTotalOverall ??
+ data?.organization?.projects?.length ??
+ data?.projects?.total ??
+ 0
+ );
+
function clearSearch() {
searchQuery?.clearInput();
}
@@ -238,7 +245,7 @@
{#if data.projects.total > 0}
{#each data.projects.projects as project}
@@ -323,13 +330,16 @@
name="Projects"
limit={data.limit}
offset={data.offset}
- total={data.projects.total} />
+ total={activeTotalOverall} />
+ currentPlan={$currentPlan}
+ archivedTotalOverall={data.archivedTotalOverall}
+ archivedOffset={data.archivedOffset}
+ limit={data.limit} />
diff --git a/src/routes/(console)/organization-[organization]/+page.ts b/src/routes/(console)/organization-[organization]/+page.ts
index dfd1d75fa..b236341d9 100644
--- a/src/routes/(console)/organization-[organization]/+page.ts
+++ b/src/routes/(console)/organization-[organization]/+page.ts
@@ -5,7 +5,6 @@ import { CARD_LIMIT, Dependencies } from '$lib/constants';
import type { PageLoad } from './$types';
import { redirect } from '@sveltejs/kit';
import { base } from '$app/paths';
-import { isCloud } from '$lib/system';
export const load: PageLoad = async ({ params, url, route, depends, parent }) => {
const { scopes } = await parent();
@@ -20,26 +19,69 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
const offset = pageToOffset(page, limit);
const search = getSearch(url);
- const projects = await sdk.forConsole.projects.list({
- queries: [
- Query.offset(offset),
- Query.equal('teamId', params.organization),
- Query.limit(limit),
- Query.orderDesc(''),
- Query.select(['$id', 'name', 'platforms', 'region', ...(isCloud ? ['status'] : [])])
- ],
- search: search || undefined
- });
+ const archivedPageRaw = parseInt(url.searchParams.get('archivedPage') || '1', 10);
+ const archivedPage =
+ Number.isFinite(archivedPageRaw) && archivedPageRaw > 0 ? archivedPageRaw : 1;
+ const archivedOffset = pageToOffset(archivedPage, limit);
+ const [activeProjects, archivedProjects, activeTotal, archivedTotal] = await Promise.all([
+ sdk.forConsole.projects.list({
+ queries: [
+ Query.offset(offset),
+ Query.equal('teamId', params.organization),
+ Query.or([Query.equal('status', 'active'), Query.isNull('status')]),
+ Query.limit(limit),
+ Query.orderDesc('')
+ ],
+ search: search || undefined
+ }),
+ sdk.forConsole.projects.list({
+ queries: [
+ Query.offset(archivedOffset),
+ Query.equal('teamId', params.organization),
+ Query.equal('status', 'archived'),
+ Query.limit(limit),
+ Query.orderDesc('')
+ ],
+ search: search || undefined
+ }),
+ sdk.forConsole.projects.list({
+ queries: [
+ Query.equal('teamId', params.organization),
+ Query.or([Query.equal('status', 'active'), Query.isNull('status')])
+ ],
+ search: search || undefined
+ }),
+ sdk.forConsole.projects.list({
+ queries: [
+ Query.equal('teamId', params.organization),
+ Query.equal('status', 'archived')
+ ],
+ search: search || undefined
+ })
+ ]);
// set `default` if no region!
- for (const project of projects.projects) {
+ for (const project of activeProjects.projects) {
+ project.region ??= 'default';
+ }
+ for (const project of archivedProjects.projects) {
project.region ??= 'default';
}
return {
offset,
limit,
- projects,
+ projects: {
+ ...activeProjects,
+ projects: activeProjects.projects,
+ total: activeTotal.total
+ },
+ activeProjectsPage: activeProjects.projects,
+ archivedProjectsPage: archivedProjects.projects,
+ activeTotalOverall: activeTotal.total,
+ archivedTotalOverall: archivedTotal.total,
+ archivedOffset,
+ archivedPage,
search
};
};
diff --git a/src/routes/(console)/organization-[organization]/billing/+page.svelte b/src/routes/(console)/organization-[organization]/billing/+page.svelte
index 99729b8e9..51d7fe353 100644
--- a/src/routes/(console)/organization-[organization]/billing/+page.svelte
+++ b/src/routes/(console)/organization-[organization]/billing/+page.svelte
@@ -9,7 +9,6 @@
import PaymentHistory from './paymentHistory.svelte';
import TaxId from './taxId.svelte';
import { failedInvoice, tierToPlan, upgradeURL, useNewPricingModal } from '$lib/stores/billing';
- import type { PaymentMethodData } from '$lib/sdk/billing';
import { onMount } from 'svelte';
import { page } from '$app/state';
import { confirmPayment } from '$lib/stores/stripe';
@@ -21,22 +20,15 @@
import { Alert } from '@appwrite.io/pink-svelte';
import { goto, invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
- import { base } from '$app/paths';
import type { PageData } from './$types';
+ import { resolve } from '$app/paths';
export let data: PageData;
- // Reactive statement to update organization when data changes
$: organization = data.organization;
-
- // why are these reactive?
- $: defaultPaymentMethod = data?.paymentMethods?.paymentMethods?.find(
- (method: PaymentMethodData) => method.$id === organization?.paymentMethodId
- );
-
- $: backupPaymentMethod = data?.paymentMethods?.paymentMethods?.find(
- (method: PaymentMethodData) => method.$id === organization?.backupPaymentMethodId
- );
+ $: baseUrl = resolve('/(console)/organization-[organization]/billing', {
+ organization: organization.$id
+ });
onMount(async () => {
if (page.url.searchParams.has('type')) {
@@ -58,7 +50,7 @@
organization.$id,
invoice.clientSecret,
organization.paymentMethodId,
- `${base}/organization-${organization.$id}/billing?type=validate-invoice&invoice=${invoice.$id}`
+ `${baseUrl}?type=validate-invoice&invoice=${invoice.$id}`
);
}
@@ -115,7 +107,7 @@
{/if}
{/if}
- {#if defaultPaymentMethod?.failed && !backupPaymentMethod}
+ {#if data.primaryPaymentMethod?.failed && !data.backupPaymentMethod}
@@ -146,7 +138,13 @@
currentInvoice={data?.billingInvoice} />
{/if}
-
+
+
+
{
depends(Dependencies.CREDIT);
depends(Dependencies.INVOICES);
depends(Dependencies.ADDRESS);
- //aggregation reloads on page param changes
+ // aggregation reloads on page param changes
depends(Dependencies.BILLING_AGGREGATION);
const billingAddressId = (organization as Organization)?.billingAddressId;
@@ -82,6 +83,7 @@ export const load: PageLoad = async ({ parent, depends, url, route }) => {
// make number
const credits = availableCredit ? availableCredit.available : null;
+ const { backup, primary } = getOrganizationPaymentMethods(organization, paymentMethods);
return {
paymentMethods,
@@ -98,6 +100,32 @@ export const load: PageLoad = async ({ parent, depends, url, route }) => {
offset: pageToOffset(
getPage(url) || 1,
getLimit(url, route, DEFAULT_BILLING_PROJECTS_LIMIT)
- )
+ ),
+
+ backupPaymentMethod: backup,
+ primaryPaymentMethod: primary
};
};
+
+function getOrganizationPaymentMethods(
+ organization: Organization,
+ paymentMethods: PaymentList
+): {
+ backup: PaymentMethodData | null;
+ primary: PaymentMethodData | null;
+} {
+ let backup: PaymentMethodData | null = null;
+ let primary: PaymentMethodData | null = null;
+
+ for (const paymentMethod of paymentMethods.paymentMethods) {
+ if (paymentMethod.$id === organization.paymentMethodId) {
+ primary = paymentMethod;
+ } else if (paymentMethod.$id === organization.backupPaymentMethodId) {
+ backup = paymentMethod;
+ }
+
+ if (primary && backup) break;
+ }
+
+ return { primary, backup };
+}
diff --git a/src/routes/(console)/organization-[organization]/billing/deleteOrgPayment.svelte b/src/routes/(console)/organization-[organization]/billing/deleteOrgPayment.svelte
index 8f6625179..1de90ac4a 100644
--- a/src/routes/(console)/organization-[organization]/billing/deleteOrgPayment.svelte
+++ b/src/routes/(console)/organization-[organization]/billing/deleteOrgPayment.svelte
@@ -25,7 +25,7 @@
message: `The payment method has been removed from ${$organization.name}`
});
trackEvent(Submit.OrganizationPaymentDelete);
- invalidate(Dependencies.ORGANIZATION);
+ await invalidate(Dependencies.PAYMENT_METHODS);
showDelete = false;
} catch (e) {
error = e.message;
@@ -34,7 +34,7 @@
showDelete = false;
}
}
- async function removeBackuptMethod() {
+ async function removeBackupMethod() {
if ($organization?.billingPlan !== BillingPlan.FREE && !hasOtherMethod) return;
showDelete = false;
@@ -45,7 +45,7 @@
message: `The payment method has been removed from ${$organization.name}`
});
trackEvent(Submit.OrganizationBackupPaymentDelete);
- invalidate(Dependencies.ORGANIZATION);
+ await invalidate(Dependencies.PAYMENT_METHODS);
showDelete = false;
} catch (e) {
error = e.message;
@@ -65,10 +65,10 @@
{:else}
+ title="Remove payment method"
+ onSubmit={isBackup ? removeBackupMethod : removeDefaultMethod}>
Are you sure you want to remove the payment method from {$organization?.name} ?
diff --git a/src/routes/(console)/organization-[organization]/billing/paymentMethods.svelte b/src/routes/(console)/organization-[organization]/billing/paymentMethods.svelte
index 124518f1e..445c65d10 100644
--- a/src/routes/(console)/organization-[organization]/billing/paymentMethods.svelte
+++ b/src/routes/(console)/organization-[organization]/billing/paymentMethods.svelte
@@ -34,16 +34,17 @@
IconTrash
} from '@appwrite.io/pink-icons-svelte';
- export let organization: Organization;
export let methods: PaymentList;
+ export let organization: Organization;
+
+ export let backupMethod: PaymentMethodData;
+ export let primaryMethod: PaymentMethodData;
let showPayment = false;
let showEdit = false;
let showDelete = false;
let showReplace = false;
let isSelectedBackup = false;
- let backupPaymentMethod: PaymentMethodData;
- let defaultPaymentMethod: PaymentMethodData;
async function addPaymentMethod(paymentMethodId: string) {
try {
@@ -56,7 +57,7 @@
message: `A new payment method has been added to ${organization.name}`
});
trackEvent(Submit.OrganizationPaymentUpdate);
- invalidate(Dependencies.ORGANIZATION);
+ await invalidate(Dependencies.PAYMENT_METHODS);
} catch (error) {
addNotification({
type: 'error',
@@ -76,7 +77,7 @@
type: 'success',
message: `A new payment method has been added to ${organization.name}`
});
- invalidate(Dependencies.ORGANIZATION);
+ await invalidate(Dependencies.PAYMENT_METHODS);
} catch (error) {
addNotification({
type: 'error',
@@ -86,27 +87,15 @@
}
}
- $: if (organization?.backupPaymentMethodId) {
- sdk.forConsole.billing
- .getOrganizationPaymentMethod(organization.$id, organization.backupPaymentMethodId)
- .then((res) => (backupPaymentMethod = res));
- }
-
- $: if (organization?.paymentMethodId) {
- sdk.forConsole.billing
- .getOrganizationPaymentMethod(organization.$id, organization.paymentMethodId)
- .then((res) => (defaultPaymentMethod = res));
- }
-
$: if (!showReplace) {
isSelectedBackup = false;
}
$: hasPaymentError =
- defaultPaymentMethod?.lastError ||
- defaultPaymentMethod?.expired ||
- backupPaymentMethod?.lastError ||
- backupPaymentMethod?.expired;
+ primaryMethod?.lastError ||
+ primaryMethod?.expired ||
+ backupMethod?.lastError ||
+ backupMethod?.expired;
@@ -132,14 +121,14 @@
-
+
- {#if defaultPaymentMethod?.userId === $user?.$id}
+ {#if primaryMethod?.userId === $user?.$id}
{
@@ -171,14 +160,14 @@
{#if organization?.backupPaymentMethodId}
-
+
- {#if backupPaymentMethod?.userId === $user?.$id}
+ {#if backupMethod?.userId === $user?.$id}
{
@@ -318,8 +307,8 @@
{/if}
{#if showEdit && isCloud && hasStripePublicKey}
+ bind:show={showEdit}
+ selectedPaymentMethod={isSelectedBackup ? backupMethod : primaryMethod} />
{/if}
{#if isCloud && hasStripePublicKey}
diff --git a/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte b/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte
index ea4cc7d26..920f7b474 100644
--- a/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte
+++ b/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte
@@ -13,17 +13,29 @@
import { PaymentBoxes } from '$lib/components/billing';
import type { PaymentMethod } from '@stripe/stripe-js';
- export let organization: Organization;
- export let show = false;
- export let isBackup = false;
- export let methods: PaymentList;
+ let {
+ show = $bindable(false),
+ isBackup = false,
+ methods,
+ organization
+ }: {
+ show?: boolean;
+ isBackup?: boolean;
+ methods: PaymentList;
+ organization: Organization;
+ } = $props();
- let name: string;
- let error: string = null;
- let selectedPaymentMethodId: string;
- let showState: boolean = false;
- let state: string = '';
- let paymentMethod: PaymentMethod | null = null;
+ let name: string | null = $state(null);
+ let error: string | null = $state(null);
+ let showState: boolean = $state(false);
+ let countryState: string | null = $state(null);
+ let paymentMethod: PaymentMethod | null = $state(null);
+ let selectedPaymentMethodId: string | null = $state(null);
+
+ const filteredMethods = $derived(methods?.paymentMethods.filter((method) => !!method?.last4));
+ const submitEvent = $derived(
+ isBackup ? Submit.OrganizationBackupPaymentUpdate : Submit.OrganizationPaymentUpdate
+ );
onMount(async () => {
if (!organization.paymentMethodId && !organization.backupPaymentMethodId) {
@@ -45,12 +57,12 @@
async function handleSubmit() {
try {
if (selectedPaymentMethodId === '$new') {
- if (showState && !state) {
+ if (showState && !countryState) {
throw Error('Please select a state');
}
let method: PaymentMethodData;
if (showState) {
- method = await setPaymentMethod(paymentMethod.id, name, state);
+ method = await setPaymentMethod(paymentMethod.id, name, countryState);
} else {
const card = await submitStripeCard(name, organization.$id);
if (card && Object.hasOwn(card, 'id')) {
@@ -69,21 +81,17 @@
? await addBackupPaymentMethod(selectedPaymentMethodId)
: await addPaymentMethod(selectedPaymentMethodId);
+ await invalidate(Dependencies.PAYMENT_METHODS);
+
addNotification({
type: 'success',
message: `Your ${isBackup ? 'backup' : 'default'} payment method has been updated`
});
- invalidate(Dependencies.ORGANIZATION);
- trackEvent(
- isBackup ? Submit.OrganizationBackupPaymentDelete : Submit.OrganizationPaymentDelete
- );
+ trackEvent(submitEvent);
show = false;
- } catch (e) {
- error = e.message;
- trackError(
- e,
- isBackup ? Submit.OrganizationBackupPaymentDelete : Submit.OrganizationPaymentDelete
- );
+ } catch (err) {
+ error = err.message;
+ trackError(err, submitEvent);
}
}
@@ -93,8 +101,8 @@
organization.$id,
paymentMethodId
);
- } catch (e) {
- error = e.message;
+ } catch (err) {
+ error = err.message;
}
}
@@ -104,23 +112,21 @@
organization.$id,
paymentMethodId
);
- } catch (e) {
- error = e.message;
+ } catch (err) {
+ error = err.message;
}
}
-
- $: filteredMethods = methods?.paymentMethods.filter((method) => !!method?.last4);
Replace the existing payment method for your organization.
{
- const promises = selectedRows.map((userId) => {
- return sdk.forProject(page.params.region, page.params.project).users.delete({ userId });
- });
+ async function handleDelete(batchDelete: DeleteOperation): Promise {
+ const result = await batchDelete((userId) =>
+ sdk.forProject(page.params.region, page.params.project).users.delete({ userId })
+ );
try {
- await Promise.all(promises);
- trackEvent(Submit.UserDelete, { total: selectedRows.length });
- } catch (error) {
- trackError(error, Submit.UserDelete);
- return error;
+ if (result.error) {
+ trackError(result.error, Submit.UserDelete);
+ } else {
+ trackEvent(Submit.UserDelete, { total: result.deleted.length });
+ }
} finally {
await invalidate(Dependencies.USERS);
}
+
+ return result;
}
diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/+page.svelte
index 48718cba3..385cfe1f5 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/security/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/security/+page.svelte
@@ -1,26 +1,23 @@
-
-
-
-
-
+
+
diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte
new file mode 100644
index 000000000..22898b060
--- /dev/null
+++ b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte
@@ -0,0 +1,159 @@
+
+
+
diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/sessionSecurity.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/sessionSecurity.svelte
new file mode 100644
index 000000000..6babbbecc
--- /dev/null
+++ b/src/routes/(console)/project-[region]-[project]/auth/security/sessionSecurity.svelte
@@ -0,0 +1,92 @@
+
+
+
diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/updatePasswordDictionary.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/updatePasswordDictionary.svelte
deleted file mode 100644
index 0e921ab04..000000000
--- a/src/routes/(console)/project-[region]-[project]/auth/security/updatePasswordDictionary.svelte
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/updatePasswordHistory.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/updatePasswordHistory.svelte
deleted file mode 100644
index 0b4218487..000000000
--- a/src/routes/(console)/project-[region]-[project]/auth/security/updatePasswordHistory.svelte
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/updatePersonalDataCheck.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/updatePersonalDataCheck.svelte
deleted file mode 100644
index 71f87c24d..000000000
--- a/src/routes/(console)/project-[region]-[project]/auth/security/updatePersonalDataCheck.svelte
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/updateSessionAlerts.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/updateSessionAlerts.svelte
deleted file mode 100644
index ba8af9ec9..000000000
--- a/src/routes/(console)/project-[region]-[project]/auth/security/updateSessionAlerts.svelte
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/updateSessionInvalidation.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/updateSessionInvalidation.svelte
deleted file mode 100644
index c2c449e3e..000000000
--- a/src/routes/(console)/project-[region]-[project]/auth/security/updateSessionInvalidation.svelte
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
diff --git a/src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
index 18b2eb2d9..8a58aec9a 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
@@ -12,6 +12,7 @@
SearchQuery,
PaginationWithLimit,
type DeleteOperationState,
+ type DeleteOperation,
MultiSelectionTable
} from '$lib/components';
import Create from '../createTeam.svelte';
@@ -44,20 +45,22 @@
);
};
- async function handleDelete(selectedRows: string[]): Promise {
- const promises = selectedRows.map((teamId) => {
- return sdk.forProject(page.params.region, page.params.project).teams.delete({ teamId });
- });
+ async function handleDelete(batchDelete: DeleteOperation): Promise {
+ const result = await batchDelete((teamId) =>
+ sdk.forProject(page.params.region, page.params.project).teams.delete({ teamId })
+ );
try {
- await Promise.all(promises);
- trackEvent(Submit.TeamDelete, { total: selectedRows.length });
- } catch (error) {
- trackError(error, Submit.TeamDelete);
- return error;
+ if (result.error) {
+ trackError(result.error, Submit.TeamDelete);
+ } else {
+ trackEvent(Submit.TeamDelete, { total: result.deleted.length });
+ }
} finally {
await invalidate(Dependencies.TEAMS);
}
+
+ return result;
}
diff --git a/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
index 98999a305..d9368560f 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
@@ -6,6 +6,7 @@
AvatarInitials,
PaginationWithLimit,
MultiSelectionTable,
+ type DeleteOperation,
type DeleteOperationState
} from '$lib/components';
import { Button } from '$lib/elements/forms';
@@ -29,23 +30,25 @@
let showDelete = $state(false);
let selectedMembership: Models.Membership | null = $state(null);
- async function handleBulkDelete(selectedRows: string[]): Promise {
- const promises = selectedRows.map((membershipId) => {
- return sdk.forProject(page.params.region, page.params.project).teams.deleteMembership({
+ async function handleBulkDelete(batchDelete: DeleteOperation): Promise {
+ const result = await batchDelete((membershipId) =>
+ sdk.forProject(page.params.region, page.params.project).teams.deleteMembership({
teamId: page.params.team,
membershipId
- });
- });
+ })
+ );
try {
- await Promise.all(promises);
- trackEvent(Submit.MembershipUpdate, { total: selectedRows.length });
- } catch (error) {
- trackError(error, Submit.MembershipUpdate);
- return error;
+ if (result.error) {
+ trackError(result.error, Submit.MembershipUpdate);
+ } else {
+ trackEvent(Submit.MembershipUpdate, { total: result.deleted.length });
+ }
} finally {
await invalidate(Dependencies.MEMBERSHIPS);
}
+
+ return result;
}
diff --git a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
index 5d9e956af..e1f053b95 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
@@ -1,5 +1,10 @@
diff --git a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
index 0c0b5525b..6f2b29355 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
@@ -4,6 +4,7 @@
import {
AvatarInitials,
type DeleteOperationState,
+ type DeleteOperation,
MultiSelectionTable
} from '$lib/components';
import { Button } from '$lib/elements/forms';
@@ -23,14 +24,14 @@
let showDelete = $state(false);
let selectedMembership: Models.Membership | null = $state(null);
- async function handleBulkDelete(selectedRows: string[]): Promise {
+ async function handleBulkDelete(batchDelete: DeleteOperation): Promise {
// Precompute a lookup map from membershipId to teamId for efficient access
const membershipIdToTeamId: Record = {};
for (const membership of data.memberships.memberships) {
membershipIdToTeamId[membership.$id] = membership.teamId;
}
- const promises = selectedRows.map((membershipId) =>
+ const result = await batchDelete((membershipId) =>
sdk.forProject(page.params.region, page.params.project).teams.deleteMembership({
teamId: membershipIdToTeamId[membershipId] || '',
membershipId
@@ -38,14 +39,16 @@
);
try {
- await Promise.all(promises);
- trackEvent(Submit.MembershipUpdate, { total: selectedRows.length });
- } catch (error) {
- trackError(error, Submit.MembershipUpdate);
- return error;
+ if (result.error) {
+ trackError(result.error, Submit.MembershipUpdate);
+ } else {
+ trackEvent(Submit.MembershipUpdate, { total: result.deleted.length });
+ }
} finally {
await invalidate(Dependencies.MEMBERSHIPS);
}
+
+ return result;
}
diff --git a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/sessions/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/sessions/+page.svelte
index b7def9138..77feda0fb 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/sessions/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/sessions/+page.svelte
@@ -1,6 +1,13 @@
diff --git a/src/routes/(console)/project-[region]-[project]/databases/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/+page.svelte
index e95c01cca..e413399e8 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/+page.svelte
@@ -14,9 +14,11 @@
import Table from './table.svelte';
import { registerCommands } from '$lib/commandCenter';
import { canWriteDatabases } from '$lib/stores/roles';
- import { Icon } from '@appwrite.io/pink-svelte';
+ import { Icon, Tooltip } from '@appwrite.io/pink-svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import EmptySearch from '$lib/components/emptySearch.svelte';
+ import { isServiceLimited } from '$lib/stores/billing';
+ import { organization } from '$lib/stores/organization';
export let data: PageData;
@@ -30,6 +32,8 @@
);
}
+ $: isLimited = isServiceLimited('databases', $organization?.billingPlan, data.databases.total);
+
$: $registerCommands([
{
label: 'Create database',
@@ -37,7 +41,7 @@
showCreate = true;
},
keys: ['c'],
- disabled: showCreate || !$canWriteDatabases,
+ disabled: showCreate || !$canWriteDatabases || isLimited,
icon: IconPlus,
group: 'databases',
rank: 10
@@ -52,10 +56,20 @@
bind:view={data.view}
searchPlaceholder="Search by name or ID">
{#if $canWriteDatabases}
- (showCreate = true)}>
-
- Create database
-
+
+
+ (showCreate = true)}>
+
+ Create database
+
+
+
+ You have reached the maximum number of databases for your plan.
+
+
{/if}
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
index d33593160..7cee5aacc 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
@@ -3,6 +3,7 @@
Card,
Confirm,
type DeleteOperationState,
+ type DeleteOperation,
Modal,
MultiSelectionTable
} from '$lib/components';
@@ -18,7 +19,7 @@
import { columns } from './store';
import { database } from '../store';
import type { BackupArchive, BackupPolicy } from '$lib/sdk/backups';
- import { Click, trackEvent } from '$lib/actions/analytics';
+ import { Click, Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { copy } from '$lib/helpers/copy';
import { LabelCard } from '$lib/components/index.js';
import DualTimeView from '$lib/components/dualTimeView.svelte';
@@ -110,39 +111,44 @@
}
}
- async function deleteBackups(selectedRows: string[]): Promise {
- const promises = selectedRows.map((archiveId) => {
- return sdk
+ async function deleteSingleBackup(archiveId: string) {
+ try {
+ await sdk
.forProject(page.params.region, page.params.project)
.backups.deleteArchive(archiveId);
- });
+
+ addNotification({
+ type: 'success',
+ message: 'Backup deleted'
+ });
+
+ showDelete = false;
+ selectedBackup = null;
+ await invalidate(Dependencies.BACKUPS);
+ } catch (error) {
+ addNotification({
+ type: 'error',
+ message: error.message
+ });
+ }
+ }
+
+ async function deleteBackups(batchDelete: DeleteOperation): Promise {
+ const result = await batchDelete((archiveId) =>
+ sdk.forProject(page.params.region, page.params.project).backups.deleteArchive(archiveId)
+ );
try {
- await Promise.all(promises);
-
- if (selectedBackup) {
- addNotification({
- type: 'success',
- message: '1 backup deleted'
- });
- }
- } catch (error) {
- if (selectedBackup) {
- addNotification({
- type: 'error',
- message: error.message
- });
+ if (result.error) {
+ trackError(result.error, Submit.DatabaseBackupDelete);
} else {
- return error;
+ trackEvent(Submit.DatabaseBackupDelete);
}
} finally {
- if (selectedBackup) {
- showDelete = false;
- selectedBackup = null;
- }
-
await invalidate(Dependencies.BACKUPS);
}
+
+ return result;
}
async function restoreBackup() {
@@ -299,7 +305,7 @@
bind:open={showDelete}
onSubmit={async () => {
if (!selectedBackup) return;
- await deleteBackups([selectedBackup.$id]);
+ await deleteSingleBackup(selectedBackup.$id);
}}>
Are you sure you want to delete the {getCleanBackupName(selectedBackup)} backup?
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/float.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/float.svelte
index bf86bf0c3..3badf6d94 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/float.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/float.svelte
@@ -86,7 +86,7 @@
label="Min"
placeholder="Enter size"
bind:value={data.min}
- step={0.1}
+ step="any"
{disabled}
required={editing} />
@@ -95,7 +95,7 @@
label="Max"
placeholder="Enter size"
bind:value={data.max}
- step={0.1}
+ step="any"
{disabled}
required={editing} />
@@ -109,7 +109,7 @@
bind:value={data.default}
disabled={data.required || data.array || disabled}
nullable={!data.required && !data.array}
- step={0.1} />
+ step="any" />
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/spreadsheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/spreadsheet.svelte
index d826c2f86..dabc3955c 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/spreadsheet.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/spreadsheet.svelte
@@ -630,7 +630,6 @@
permissions: row.$permissions
});
- invalidate(Dependencies.ROW);
trackEvent(Submit.RowUpdate);
addNotification({
message: 'Row has been updated',
@@ -1105,7 +1104,14 @@
{
+ const success = await updateRowContents(row);
+ if (success) {
+ // database update succeeded!
+ paginatedRows.update(index, row);
+ }
+ return success;
+ }}
noInlineEdit={isRelatedToMany && hasItems}
onChange={(row) => paginatedRows.update(index, row)}
onRevert={(row) => paginatedRows.update(index, row)}
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/store.ts
index 6c6a28489..913bba1e3 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/store.ts
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/store.ts
@@ -193,7 +193,9 @@ export const expandTabs = writable(null);
export const spreadsheetRenderKey = writable('initial');
export const paginatedRowsLoading = writable(false);
-export const paginatedRows = createSparsePagedDataStore(SPREADSHEET_PAGE_LIMIT);
+export const paginatedRows = createSparsePagedDataStore(
+ SPREADSHEET_PAGE_LIMIT
+);
export const PROHIBITED_ROW_KEYS = [
'$id',
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
index fa19cde37..6a5c73352 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
@@ -3,7 +3,12 @@
import { resolve } from '$app/paths';
import { page } from '$app/state';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
- import { Id, MultiSelectionTable, type DeleteOperationState } from '$lib/components';
+ import {
+ Id,
+ MultiSelectionTable,
+ type DeleteOperation,
+ type DeleteOperationState
+ } from '$lib/components';
import { Dependencies } from '$lib/constants';
import DualTimeView from '$lib/components/dualTimeView.svelte';
import { canWriteTables } from '$lib/stores/roles';
@@ -20,23 +25,26 @@
data: PageData;
} = $props();
- async function onDelete(selectedTables: string[]): Promise {
- const promises = selectedTables.map((tableId) =>
+ async function onDelete(batchDelete: DeleteOperation): Promise {
+ const result = await batchDelete((tableId) =>
sdk.forProject(page.params.region, page.params.project).tablesDB.deleteTable({
databaseId: page.params.database,
tableId
})
);
+
try {
- await Promise.all(promises);
- trackEvent(Submit.TableDelete);
- } catch (error) {
- trackError(error, Submit.TableDelete);
- return error;
+ if (result.error) {
+ trackError(result.error, Submit.TableDelete);
+ } else {
+ trackEvent(Submit.TableDelete, { total: result.deleted.length });
+ }
} finally {
await invalidate(Dependencies.TABLES);
subNavigation.update();
}
+
+ return result;
}
function getTableHref(table: Models.Table) {
diff --git a/src/routes/(console)/project-[region]-[project]/databases/table.svelte b/src/routes/(console)/project-[region]-[project]/databases/table.svelte
index 10ec6f307..0f88682d9 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/table.svelte
@@ -2,7 +2,7 @@
import { base } from '$app/paths';
import { page } from '$app/state';
import { Id } from '$lib/components';
- import { toLocaleDateTime } from '$lib/helpers/date';
+ import DualTimeView from '$lib/components/dualTimeView.svelte';
import { columns } from './store';
import { IconExclamation } from '@appwrite.io/pink-icons-svelte';
import { Layout, Tooltip, Table, Icon } from '@appwrite.io/pink-svelte';
@@ -72,8 +72,10 @@
{`Last backup: ${lastBackup}`}
+ {:else if column.type === 'datetime'}
+
{:else}
- {toLocaleDateTime(database[column.id])}
+ {database[column.id]}
{/if}
{/each}
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/+page.ts b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/+page.ts
index 4145bf828..5fdc16ceb 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/+page.ts
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/+page.ts
@@ -29,6 +29,7 @@ export const load: PageLoad = async ({ depends, params, url, route, parent }) =>
Query.limit(limit),
Query.offset(offset),
Query.orderDesc(''),
+ Query.orderDesc('$updatedAt'),
...parsedQueries.values()
],
search: search || undefined
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/store.ts b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/store.ts
index f741c03ee..5f36c6bc1 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/store.ts
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/store.ts
@@ -15,5 +15,12 @@ export const columns = writable([
title: 'Target',
type: 'string',
width: { min: 120, max: 400 }
+ },
+
+ {
+ id: 'updated',
+ title: '',
+ type: 'string',
+ width: { min: 160, max: 180 }
}
]);
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/table.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/table.svelte
index 98e29e5ba..83daf1ffe 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/table.svelte
@@ -25,6 +25,7 @@
import { regionalProtocol } from '$routes/(console)/project-[region]-[project]/store';
import DnsRecordsAction from '$lib/components/domains/dnsRecordsAction.svelte';
import ViewLogsModal from '$lib/components/domains/viewLogsModal.svelte';
+ import { timeFromNowShort } from '$lib/helpers/date';
let {
proxyRules,
@@ -46,6 +47,28 @@
? 'Deployed from ' + proxy.deploymentVcsProviderBranch
: 'Active deployment';
};
+
+ function updatedLabel(proxyRule: Models.ProxyRule): string {
+ if (proxyRule.status === 'verified') {
+ return '';
+ }
+
+ const timeStr = timeFromNowShort(proxyRule.$updatedAt);
+ if (timeStr === 'n/a') {
+ return '';
+ }
+
+ const prefix =
+ proxyRule.status === 'created'
+ ? 'Checked'
+ : proxyRule.status === 'verifying'
+ ? 'Updated'
+ : proxyRule.status === 'unverified'
+ ? 'Failed'
+ : '';
+
+ return prefix + ' ' + timeStr;
+ }
@@ -117,6 +140,15 @@
{:else if column.id === 'target'}
{proxyTarget(proxyRule)}
+ {:else if column.id === 'updated' && proxyRule.status !== 'verified'}
+
+
+ {updatedLabel(proxyRule)}
+
+
{/if}
{/each}
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
index f681c04ef..1e6915c38 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
@@ -1,5 +1,10 @@
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
index 7b46acc09..94672a8d6 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
@@ -1,5 +1,10 @@
diff --git a/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte b/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
index b096f2561..464237e79 100644
--- a/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
@@ -3,6 +3,7 @@
import { page } from '$app/state';
import {
type DeleteOperationState,
+ type DeleteOperation,
Empty,
EmptyFilter,
EmptySearch,
@@ -99,22 +100,24 @@
}
]);
- async function handleDelete(selectedRows: string[]): Promise {
- const promises = selectedRows.map((id) =>
+ async function handleDelete(batchDelete: DeleteOperation): Promise {
+ const result = await batchDelete((id) =>
sdk
.forProject(page.params.region, page.params.project)
.messaging.delete({ messageId: id })
);
try {
- await Promise.all(promises);
- trackEvent(Submit.MessagingMessageDelete, { total: selectedRows.length });
- } catch (error) {
- trackError(error, Submit.MessagingMessageDelete);
- return error;
+ if (result.error) {
+ trackError(result.error, Submit.MessagingMessageDelete);
+ } else {
+ trackEvent(Submit.MessagingMessageDelete, { total: result.deleted.length });
+ }
} finally {
await invalidate(Dependencies.MESSAGING_MESSAGES);
}
+
+ return result;
}
onMount(() => {
diff --git a/src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte b/src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte
index a54178b3d..f7ed22281 100644
--- a/src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte
@@ -1,7 +1,12 @@
diff --git a/src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte b/src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
index f5525754e..709529b0f 100644
--- a/src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
@@ -1,7 +1,12 @@
diff --git a/src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte b/src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
index c9a64e9af..113d9d76e 100644
--- a/src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
@@ -2,7 +2,12 @@
import { invalidate } from '$app/navigation';
import { base } from '$app/paths';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
- import { type DeleteOperationState, Id, MultiSelectionTable } from '$lib/components';
+ import {
+ type DeleteOperationState,
+ type DeleteOperation,
+ Id,
+ MultiSelectionTable
+ } from '$lib/components';
import { Dependencies } from '$lib/constants';
import type { PageData } from './$types';
import ProviderType from '../../providerType.svelte';
@@ -32,8 +37,8 @@
return record;
});
- async function handleDelete(selectedRows: string[]): Promise {
- async function deleteSubscriber(subscriberId: string) {
+ async function handleDelete(batchDelete: DeleteOperation): Promise {
+ const result = await batchDelete(async (subscriberId) => {
await sdk
.forProject(page.params.region, page.params.project)
.messaging.deleteSubscriber({
@@ -44,19 +49,19 @@
const { target } = subscribers[subscriberId];
const { [target.$id]: _, ...rest } = $targetsById;
$targetsById = rest;
- }
-
- const promises = selectedRows.map((id) => deleteSubscriber(id));
+ });
try {
- await Promise.all(promises);
- trackEvent(Submit.MessagingTopicSubscriberDelete, { total: selectedRows.length });
- } catch (error) {
- trackError(error, Submit.MessagingTopicSubscriberDelete);
- return error;
+ if (result.error) {
+ trackError(result.error, Submit.MessagingTopicSubscriberDelete);
+ } else {
+ trackEvent(Submit.MessagingTopicSubscriberDelete, { total: result.deleted.length });
+ }
} finally {
await invalidate(Dependencies.MESSAGING_TOPIC_SUBSCRIBERS);
}
+
+ return result;
}
diff --git a/src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte b/src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
index 9cfb56a83..535be9bd6 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
@@ -67,7 +67,7 @@
allowSelection={$canWriteKeys}
showSuccessNotification={false}
resource={`${capitalize(label)} key`}
- onDelete={(selectedRows) => {
+ onDelete={(_, selectedRows) => {
showDeleteModal = true;
selectedKeys = selectedRows;
}}>
diff --git a/src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte b/src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
index 22d0bce77..de3cbcb2f 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
@@ -81,7 +81,11 @@
import { page } from '$app/state';
import type { PageProps } from './$types';
import type { Models } from '@appwrite.io/console';
- import { type DeleteOperationState, MultiSelectionTable } from '$lib/components';
+ import {
+ type DeleteOperationState,
+ type DeleteOperation,
+ MultiSelectionTable
+ } from '$lib/components';
import { sdk } from '$lib/stores/sdk';
import { Submit, trackError } from '$lib/actions/analytics';
import { invalidate } from '$app/navigation';
@@ -129,24 +133,26 @@
}
async function handlePlatformDelete(
- selectedPlatforms: string[]
+ batchDelete: DeleteOperation
): Promise {
- const promises = selectedPlatforms.map((platformId) => {
- return sdk.forConsole.projects.deletePlatform({
+ const result = await batchDelete((platformId) =>
+ sdk.forConsole.projects.deletePlatform({
projectId: page.params.project,
platformId
- });
- });
+ })
+ );
try {
- await Promise.all(promises);
- trackEvent(Submit.PlatformDelete, { total: selectedPlatforms.length });
- } catch (error) {
- trackError(error, Submit.PlatformDelete);
- return error;
+ if (result.error) {
+ trackError(result.error, Submit.PlatformDelete);
+ } else {
+ trackEvent(Submit.PlatformDelete, { total: result.deleted.length });
+ }
} finally {
await invalidate(Dependencies.PROJECT);
}
+
+ return result;
}
setOverviewAction(Action);
diff --git a/src/routes/(console)/project-[region]-[project]/settings/domains/+page.ts b/src/routes/(console)/project-[region]-[project]/settings/domains/+page.ts
index 39495486c..bf7570e23 100644
--- a/src/routes/(console)/project-[region]-[project]/settings/domains/+page.ts
+++ b/src/routes/(console)/project-[region]-[project]/settings/domains/+page.ts
@@ -21,7 +21,11 @@ export const load: PageLoad = async ({ depends, url, route, params, parent }) =>
const { organization } = await parent();
const rules = await sdk.forProject(params.region, params.project).proxy.listRules({
- queries: [Query.equal('type', RuleType.API), Query.equal('trigger', RuleTrigger.MANUAL)],
+ queries: [
+ Query.equal('type', RuleType.API),
+ Query.equal('trigger', RuleTrigger.MANUAL),
+ Query.orderDesc('$updatedAt')
+ ],
search: search || undefined
});
diff --git a/src/routes/(console)/project-[region]-[project]/settings/domains/table.svelte b/src/routes/(console)/project-[region]-[project]/settings/domains/table.svelte
index 594988a7f..051a20d3a 100644
--- a/src/routes/(console)/project-[region]-[project]/settings/domains/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/settings/domains/table.svelte
@@ -24,6 +24,7 @@
import { regionalProtocol } from '../../store';
import DnsRecordsAction from '$lib/components/domains/dnsRecordsAction.svelte';
import ViewLogsModal from '$lib/components/domains/viewLogsModal.svelte';
+ import { timeFromNowShort } from '$lib/helpers/date';
let {
domains,
@@ -45,8 +46,36 @@
type: 'string',
format: 'string',
width: { min: 300, max: 550 }
+ },
+ {
+ id: 'updated',
+ title: '',
+ type: 'string',
+ width: { min: 160, max: 180 }
}
];
+
+ function updatedLabel(proxyRule: Models.ProxyRule): string {
+ if (proxyRule.status === 'verified') {
+ return '';
+ }
+
+ const timeStr = timeFromNowShort(proxyRule.$updatedAt);
+ if (timeStr === 'n/a') {
+ return '';
+ }
+
+ const prefix =
+ proxyRule.status === 'created'
+ ? 'Checked'
+ : proxyRule.status === 'verifying'
+ ? 'Updated'
+ : proxyRule.status === 'unverified'
+ ? 'Failed'
+ : '';
+
+ return prefix + ' ' + timeStr;
+ }
@@ -116,6 +145,15 @@
{/if}
+ {:else if column.id === 'updated' && proxyRule.status !== 'verified'}
+
+
+ {updatedLabel(proxyRule)}
+
+
{/if}
{/each}
diff --git a/src/routes/(console)/project-[region]-[project]/settings/webhooks/+page.svelte b/src/routes/(console)/project-[region]-[project]/settings/webhooks/+page.svelte
index 4ee496dec..e7ec5e8d1 100644
--- a/src/routes/(console)/project-[region]-[project]/settings/webhooks/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/settings/webhooks/+page.svelte
@@ -2,7 +2,7 @@
import { base } from '$app/paths';
import { page } from '$app/state';
import { Empty, Id } from '$lib/components';
- import { toLocaleDateTime } from '$lib/helpers/date';
+ import DualTimeView from '$lib/components/dualTimeView.svelte';
import { Container } from '$lib/layout';
import { Button } from '$lib/elements/forms';
import type { PageData } from './$types';
@@ -58,7 +58,11 @@
{:else if column.id === 'events'}
{webhook.events.length}
{:else if column.type === 'datetime'}
- {webhook[column.id] ? toLocaleDateTime(webhook[column.id]) : '-'}
+ {#if webhook[column.id]}
+
+ {:else}
+ -
+ {/if}
{:else if column.id === 'enabled'}
- import { type DeleteOperationState, Id, MultiSelectionTable } from '$lib/components';
+ import {
+ type DeleteOperationState,
+ type DeleteOperation,
+ Id,
+ MultiSelectionTable
+ } from '$lib/components';
import type { PageData } from './$types';
import { type Models } from '@appwrite.io/console';
import { formatTimeDetailed } from '$lib/helpers/timeConversion';
@@ -37,25 +42,28 @@
let selectedDeployment: Models.Deployment | null = $state(null);
- async function deleteDeployments(selectedRows: string[]): Promise {
- const promises = selectedRows.map((deploymentId) =>
+ async function deleteDeployments(batchDelete: DeleteOperation): Promise {
+ const result = await batchDelete((deploymentId) =>
sdk.forProject(page.params.region, page.params.project).sites.deleteDeployment({
siteId: page.params.site,
deploymentId
})
);
+
try {
- await Promise.all(promises);
- trackEvent(Submit.DeploymentDelete);
- } catch (error) {
- trackError(error, Submit.DeploymentDelete);
- return error;
+ if (result.error) {
+ trackError(result.error, Submit.DeploymentDelete);
+ } else {
+ trackEvent(Submit.DeploymentDelete, { total: result.deleted.length });
+ }
} finally {
await Promise.all([
invalidate(Dependencies.DEPLOYMENTS),
invalidate(Dependencies.SITE)
]);
}
+
+ return result;
}
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/+page.ts b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/+page.ts
index a942cc3c0..25c021aa6 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/+page.ts
+++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/+page.ts
@@ -28,6 +28,7 @@ export const load = async ({ params, depends, url, route, parent }) => {
Query.limit(limit),
Query.offset(offset),
Query.orderDesc(''),
+ Query.orderDesc('$updatedAt'),
...parsedQueries.values()
],
search: search || undefined
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/store.ts b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/store.ts
index f741c03ee..5f36c6bc1 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/store.ts
+++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/store.ts
@@ -15,5 +15,12 @@ export const columns = writable([
title: 'Target',
type: 'string',
width: { min: 120, max: 400 }
+ },
+
+ {
+ id: 'updated',
+ title: '',
+ type: 'string',
+ width: { min: 160, max: 180 }
}
]);
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/table.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/table.svelte
index b89528517..a3921aeaa 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/table.svelte
@@ -25,6 +25,7 @@
import { regionalProtocol } from '$routes/(console)/project-[region]-[project]/store';
import DnsRecordsAction from '$lib/components/domains/dnsRecordsAction.svelte';
import ViewLogsModal from '$lib/components/domains/viewLogsModal.svelte';
+ import { timeFromNowShort } from '$lib/helpers/date';
let {
proxyRules,
@@ -46,6 +47,28 @@
? 'Deployed from ' + proxy.deploymentVcsProviderBranch
: 'Active deployment';
};
+
+ function updatedLabel(proxyRule: Models.ProxyRule): string {
+ if (proxyRule.status === 'verified') {
+ return '';
+ }
+
+ const timeStr = timeFromNowShort(proxyRule.$updatedAt);
+ if (timeStr === 'n/a') {
+ return '';
+ }
+
+ const prefix =
+ proxyRule.status === 'created'
+ ? 'Checked'
+ : proxyRule.status === 'verifying'
+ ? 'Updated'
+ : proxyRule.status === 'unverified'
+ ? 'Failed'
+ : '';
+
+ return prefix + ' ' + timeStr;
+ }
@@ -117,6 +140,15 @@
{:else if column.id === 'target'}
{proxyTarget(proxyRule)}
+ {:else if column.id === 'updated' && proxyRule.status !== 'verified'}
+
+
+ {updatedLabel(proxyRule)}
+
+
{/if}
{/each}
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
index da4890035..de2504715 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
@@ -1,5 +1,10 @@
diff --git a/src/routes/(console)/project-[region]-[project]/sites/table.svelte b/src/routes/(console)/project-[region]-[project]/sites/table.svelte
index 680c82d38..3f15ba97d 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/table.svelte
@@ -1,7 +1,7 @@