From 2a058be69ee0a86a2867ca5ecf4d6b18345c4241 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Tue, 2 Sep 2025 19:38:40 +0530 Subject: [PATCH 01/49] fix org projects pagination and add archived pagination --- src/lib/components/archiveProject.svelte | 20 ++++++++---- .../organization-[organization]/+page.svelte | 13 +++++--- .../organization-[organization]/+page.ts | 31 +++++++++++++++++-- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/lib/components/archiveProject.svelte b/src/lib/components/archiveProject.svelte index 3ebb938c1..e2858acbc 100644 --- a/src/lib/components/archiveProject.svelte +++ b/src/lib/components/archiveProject.svelte @@ -1,6 +1,6 @@ @@ -160,8 +160,14 @@
- - {#each projectsToArchive as project} + + {#snippet children(items)} + + {#each items as project} {@const platforms = filterPlatforms( project.platforms.map((platform) => getPlatformInfo(platform.type)) )} @@ -255,8 +261,10 @@ {/if} - {/each} - + {/each} + + {/snippet} +
diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index 27af7cbff..7e958cc69 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -104,9 +104,14 @@ return project.status !== 'active'; } - $: projectsToArchive = data.projects.projects.filter((project) => project.status !== 'active'); + $: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter( + (project) => project.status !== 'active' + ); - $: activeProjects = data.projects.projects.filter((project) => project.status === 'active'); + $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter( + (project) => project.status === 'active' + ); + $: activeTotalOverall = data?.activeTotalOverall ?? data?.organization?.projects?.length ?? data?.projects?.total ?? 0; function clearSearch() { searchQuery?.clearInput(); } @@ -157,7 +162,7 @@ {#if activeProjects.length > 0} {#each activeProjects as project} @@ -242,7 +247,7 @@ name="Projects" limit={data.limit} offset={data.offset} - total={data.projects.total} /> + total={activeTotalOverall} /> search: search || undefined }); + let allProjects: typeof projects.projects = []; + let fetchedCount = 0; + const total = projects.total; + + while (fetchedCount < total) { + const next = await sdk.forConsole.projects.list({ + queries: [ + Query.offset(fetchedCount), + Query.equal('teamId', params.organization), + Query.limit(limit), + Query.orderDesc('') + ], + search: search || undefined + }); + allProjects = allProjects.concat(next.projects); + fetchedCount += next.projects.length; + if (next.projects.length === 0) break; + } + + const allActiveProjects = allProjects.filter((p) => p.status === 'active'); + const allArchivedProjects = allProjects.filter((p) => p.status !== 'active'); + + const activeProjectsForPage = allActiveProjects.slice(offset, offset + limit); + // set `default` if no region! - for (const project of projects.projects) { + for (const project of allProjects) { project.region ??= 'default'; } return { offset, limit, - projects, + projects: { ...projects, projects: allProjects, total: allActiveProjects.length }, + activeProjectsPage: activeProjectsForPage, + archivedProjectsPage: allArchivedProjects, + activeTotalOverall: allActiveProjects.length, search }; }; From 82cc38d49cbe84d63dab7f4d3b0de218affa4ec2 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Tue, 2 Sep 2025 19:43:04 +0530 Subject: [PATCH 02/49] lint issue --- src/lib/components/archiveProject.svelte | 190 +++++++++--------- .../organization-[organization]/+page.svelte | 6 +- .../organization-[organization]/+page.ts | 2 +- 3 files changed, 106 insertions(+), 92 deletions(-) diff --git a/src/lib/components/archiveProject.svelte b/src/lib/components/archiveProject.svelte index e2858acbc..0fdd91c33 100644 --- a/src/lib/components/archiveProject.svelte +++ b/src/lib/components/archiveProject.svelte @@ -168,99 +168,109 @@ {#snippet children(items)} {#each items as project} - {@const platforms = filterPlatforms( - project.platforms.map((platform) => getPlatformInfo(platform.type)) - )} - {@const formatted = formatName(project.name)} - - - {project?.platforms?.length ? project?.platforms?.length : 'No'} apps - - {formatted} - -
- - { - e.preventDefault(); - e.stopPropagation(); - readOnlyInfoOpen = { - ...readOnlyInfoOpen, - [project.$id]: !readOnlyInfoOpen[project.$id] - }; - }}> - - Read only - - -
  • - - Archived projects are read-only. You can view - and migrate their data, but they no longer - accept edits or requests. - -
  • -
    -
    - - - - handleUnarchiveProject(project)} - >Unarchive project - handleMigrateProject(project)} - >Migrate project - - -
    -
    + {@const platforms = filterPlatforms( + project.platforms.map((platform) => + getPlatformInfo(platform.type) + ) + )} + {@const formatted = formatName(project.name)} + + + {project?.platforms?.length + ? project?.platforms?.length + : 'No'} apps + + {formatted} + +
    + + { + e.preventDefault(); + e.stopPropagation(); + readOnlyInfoOpen = { + ...readOnlyInfoOpen, + [project.$id]: + !readOnlyInfoOpen[project.$id] + }; + }}> + + Read only + + +
  • + + Archived projects are read-only. You can + view and migrate their data, but they no + longer accept edits or requests. + +
  • +
    +
    + + + + + handleUnarchiveProject(project)} + >Unarchive project + + handleMigrateProject(project)} + >Migrate project + + +
    +
    - {#each platforms.slice(0, 2) as platform} - {@const icon = getIconForPlatform(platform.icon)} - - - - {/each} + {#each platforms.slice(0, 2) as platform} + {@const icon = getIconForPlatform(platform.icon)} + + + + {/each} - {#if platforms.length > 3} - - {/if} + {#if platforms.length > 3} + + {/if} - - {#if isCloud && $regionsStore?.regions} - {@const region = findRegion(project)} - {region?.name} - {/if} - -
    + + {#if isCloud && $regionsStore?.regions} + {@const region = findRegion(project)} + {region?.name} + {/if} + +
    {/each}
    {/snippet} diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index 7e958cc69..683f0be6a 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -111,7 +111,11 @@ $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter( (project) => project.status === 'active' ); - $: activeTotalOverall = data?.activeTotalOverall ?? data?.organization?.projects?.length ?? data?.projects?.total ?? 0; + $: activeTotalOverall = + data?.activeTotalOverall ?? + data?.organization?.projects?.length ?? + data?.projects?.total ?? + 0; function clearSearch() { searchQuery?.clearInput(); } diff --git a/src/routes/(console)/organization-[organization]/+page.ts b/src/routes/(console)/organization-[organization]/+page.ts index b640aaba4..e7d97ed24 100644 --- a/src/routes/(console)/organization-[organization]/+page.ts +++ b/src/routes/(console)/organization-[organization]/+page.ts @@ -49,7 +49,7 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) => const allActiveProjects = allProjects.filter((p) => p.status === 'active'); const allArchivedProjects = allProjects.filter((p) => p.status !== 'active'); - + const activeProjectsForPage = allActiveProjects.slice(offset, offset + limit); // set `default` if no region! From 11e460c89bbb67760e11512d7ee65f830cf83d2b Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Wed, 3 Sep 2025 11:53:54 +0530 Subject: [PATCH 03/49] fix: correct off-by-one error in platform badge conditional rendering --- src/lib/components/archiveProject.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/archiveProject.svelte b/src/lib/components/archiveProject.svelte index 0fdd91c33..c2b514d16 100644 --- a/src/lib/components/archiveProject.svelte +++ b/src/lib/components/archiveProject.svelte @@ -257,7 +257,7 @@ {/each} - {#if platforms.length > 3} + {#if platforms.length > 2} Date: Tue, 9 Sep 2025 12:33:56 +0530 Subject: [PATCH 04/49] feat(org-projects): add separate pagination for archived projects and optimize queries --- src/lib/components/archiveProject.svelte | 236 +++++++++--------- src/lib/components/archivedLimit.svelte | 44 ++++ src/lib/components/archivedPagination.svelte | 23 ++ .../archivedPaginationWithLimit.svelte | 31 +++ .../organization-[organization]/+page.svelte | 5 +- .../organization-[organization]/+page.ts | 71 ++++-- 6 files changed, 266 insertions(+), 144 deletions(-) create mode 100644 src/lib/components/archivedLimit.svelte create mode 100644 src/lib/components/archivedPagination.svelte create mode 100644 src/lib/components/archivedPaginationWithLimit.svelte diff --git a/src/lib/components/archiveProject.svelte b/src/lib/components/archiveProject.svelte index c2b514d16..a2b096073 100644 --- a/src/lib/components/archiveProject.svelte +++ b/src/lib/components/archiveProject.svelte @@ -1,6 +1,7 @@ + + + +

    + {name} per page. Total: {sum >= 5000 ? `${sum}+` : sum} +

    +
    diff --git a/src/lib/components/archivedPagination.svelte b/src/lib/components/archivedPagination.svelte new file mode 100644 index 000000000..ae3a71404 --- /dev/null +++ b/src/lib/components/archivedPagination.svelte @@ -0,0 +1,23 @@ + + + diff --git a/src/lib/components/archivedPaginationWithLimit.svelte b/src/lib/components/archivedPaginationWithLimit.svelte new file mode 100644 index 000000000..7b5d694c0 --- /dev/null +++ b/src/lib/components/archivedPaginationWithLimit.svelte @@ -0,0 +1,31 @@ + + + + {#if showLimit} + + {/if} + + + diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index 683f0be6a..df5dc380c 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -257,7 +257,10 @@ + 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 e7d97ed24..bf1e20408 100644 --- a/src/routes/(console)/organization-[organization]/+page.ts +++ b/src/routes/(console)/organization-[organization]/+page.ts @@ -18,52 +18,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({ + const archivedPage = parseInt(url.searchParams.get('archivedPage') || '1'); + const archivedOffset = pageToOffset(archivedPage, limit); + + // fetch active projects with offset set + const activeProjects = await 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 }); - let allProjects: typeof projects.projects = []; - let fetchedCount = 0; - const total = projects.total; + // Fetch archived projects with separate pagination + const archivedProjects = await 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 + }); - while (fetchedCount < total) { - const next = await sdk.forConsole.projects.list({ - queries: [ - Query.offset(fetchedCount), - Query.equal('teamId', params.organization), - Query.limit(limit), - Query.orderDesc('') - ], - search: search || undefined - }); - allProjects = allProjects.concat(next.projects); - fetchedCount += next.projects.length; - if (next.projects.length === 0) break; - } + // get total counts + const activeTotal = await sdk.forConsole.projects.list({ + queries: [ + Query.equal('teamId', params.organization), + Query.or([Query.equal('status', 'active'), Query.isNull('status')]) + ], + search: search || undefined + }); - const allActiveProjects = allProjects.filter((p) => p.status === 'active'); - const allArchivedProjects = allProjects.filter((p) => p.status !== 'active'); - - const activeProjectsForPage = allActiveProjects.slice(offset, offset + limit); + const archivedTotal = await 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 allProjects) { + for (const project of activeProjects.projects) { + project.region ??= 'default'; + } + for (const project of archivedProjects.projects) { project.region ??= 'default'; } return { offset, limit, - projects: { ...projects, projects: allProjects, total: allActiveProjects.length }, - activeProjectsPage: activeProjectsForPage, - archivedProjectsPage: allArchivedProjects, - activeTotalOverall: allActiveProjects.length, + projects: { + ...activeProjects, + projects: activeProjects.projects, + total: activeTotal.total + }, + activeProjectsPage: activeProjects.projects, + archivedProjectsPage: archivedProjects.projects, + activeTotalOverall: activeTotal.total, + archivedTotalOverall: archivedTotal.total, + archivedOffset, + archivedPage, search }; }; From 655b053c37011afde900e07dc751ea176360157d Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Tue, 9 Sep 2025 12:41:26 +0530 Subject: [PATCH 05/49] fix: lint issues --- .../organization-[organization]/+page.svelte | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index 31c8e492a..46b8f2769 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -109,24 +109,19 @@ return project.status === 'archived'; } -$: projectsToArchive = - (data.archivedProjectsPage ?? data.projects.projects).filter( - (project) => - isCloud - ? project.status === 'archived' - : project.status !== 'active' // fallback for non-cloud + $: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter( + (project) => (isCloud ? project.status === 'archived' : project.status !== 'active') // fallback for non-cloud ); -$: activeProjects = - (data.activeProjectsPage ?? data.projects.projects).filter( + $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter( (project) => project.status === 'active' ); -$: activeTotalOverall = - data?.activeTotalOverall ?? - data?.organization?.projects?.length ?? - data?.projects?.total ?? - 0; + $: activeTotalOverall = + data?.activeTotalOverall ?? + data?.organization?.projects?.length ?? + data?.projects?.total ?? + 0; function clearSearch() { searchQuery?.clearInput(); } From 4f4c679e7cef1e750c95ed77e8e095f431b3156f Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:46:31 +0530 Subject: [PATCH 06/49] fixes --- src/lib/components/archiveProject.svelte | 19 ++--- src/lib/components/archivedLimit.svelte | 44 ----------- src/lib/components/archivedPagination.svelte | 23 ------ .../archivedPaginationWithLimit.svelte | 31 -------- src/lib/components/limit.svelte | 13 +++- src/lib/components/pagination.svelte | 10 ++- src/lib/components/paginationWithLimit.svelte | 21 +++++- src/lib/components/paginator.svelte | 6 +- .../organization-[organization]/+page.svelte | 2 +- .../organization-[organization]/+page.ts | 73 +++++++++---------- 10 files changed, 86 insertions(+), 156 deletions(-) delete mode 100644 src/lib/components/archivedLimit.svelte delete mode 100644 src/lib/components/archivedPagination.svelte delete mode 100644 src/lib/components/archivedPaginationWithLimit.svelte diff --git a/src/lib/components/archiveProject.svelte b/src/lib/components/archiveProject.svelte index 2cfa7c606..d7c5b6a1b 100644 --- a/src/lib/components/archiveProject.svelte +++ b/src/lib/components/archiveProject.svelte @@ -1,5 +1,5 @@ - - - -

    - {name} per page. Total: {sum >= 5000 ? `${sum}+` : sum} -

    -
    diff --git a/src/lib/components/archivedPagination.svelte b/src/lib/components/archivedPagination.svelte deleted file mode 100644 index ae3a71404..000000000 --- a/src/lib/components/archivedPagination.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - - diff --git a/src/lib/components/archivedPaginationWithLimit.svelte b/src/lib/components/archivedPaginationWithLimit.svelte deleted file mode 100644 index 7b5d694c0..000000000 --- a/src/lib/components/archivedPaginationWithLimit.svelte +++ /dev/null @@ -1,31 +0,0 @@ - - - - {#if showLimit} - - {/if} - - - diff --git a/src/lib/components/limit.svelte b/src/lib/components/limit.svelte index c8e72a401..8a7c1ef67 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,15 @@ url.searchParams.set('limit', limit.toString()); await preferences.setLimit(limit); - if (url.searchParams.has('page')) { - const page = Number(url.searchParams.get('page')); + if (url.searchParams.has(pageParam)) { + const page = Number(url.searchParams.get(pageParam)); const newPage = Math.floor(((page - 1) * previousLimit) / limit); - url.searchParams.set('page', newPage.toString()); + 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/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..7522f3521 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]: any; } = $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..8a1b2b6de 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]: any; } = $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/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index 46b8f2769..9fb546837 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -110,7 +110,7 @@ } $: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter( - (project) => (isCloud ? project.status === 'archived' : project.status !== 'active') // fallback for non-cloud + (project) => project.status === 'archived' ); $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter( diff --git a/src/routes/(console)/organization-[organization]/+page.ts b/src/routes/(console)/organization-[organization]/+page.ts index bf1e20408..ce972e3ba 100644 --- a/src/routes/(console)/organization-[organization]/+page.ts +++ b/src/routes/(console)/organization-[organization]/+page.ts @@ -21,43 +21,42 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) => const archivedPage = parseInt(url.searchParams.get('archivedPage') || '1'); const archivedOffset = pageToOffset(archivedPage, limit); - // fetch active projects with offset set - const activeProjects = await 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 - }); - - // Fetch archived projects with separate pagination - const archivedProjects = await 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 - }); - - // get total counts - const activeTotal = await sdk.forConsole.projects.list({ - queries: [ - Query.equal('teamId', params.organization), - Query.or([Query.equal('status', 'active'), Query.isNull('status')]) - ], - search: search || undefined - }); - - const archivedTotal = await sdk.forConsole.projects.list({ - queries: [Query.equal('teamId', params.organization), Query.equal('status', 'archived')], - search: search || undefined - }); + 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 activeProjects.projects) { From 7287571c505491f169437a2db3a0fe1f50c1f364 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:55:32 +0530 Subject: [PATCH 07/49] lint issue --- src/lib/components/paginationWithLimit.svelte | 2 +- src/lib/components/paginator.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/paginationWithLimit.svelte b/src/lib/components/paginationWithLimit.svelte index 7522f3521..b48fbc0f6 100644 --- a/src/lib/components/paginationWithLimit.svelte +++ b/src/lib/components/paginationWithLimit.svelte @@ -20,7 +20,7 @@ useCreateLink?: boolean; pageParam?: string; removeOnFirstPage?: boolean; - [key: string]: any; + [key: string]: unknown; } = $props(); const showLimit = $derived(!!useCreateLink); diff --git a/src/lib/components/paginator.svelte b/src/lib/components/paginator.svelte index 8a1b2b6de..053e2ea14 100644 --- a/src/lib/components/paginator.svelte +++ b/src/lib/components/paginator.svelte @@ -27,7 +27,7 @@ | undefined; offset?: number; children: Snippet<[T[], number]>; - [key: string]: any; + [key: string]: unknown; } = $props(); let total = $derived(items.length); From 6ad7ee10c51d556fd563e5f253ca0b4f5eb10f29 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:00:39 +0530 Subject: [PATCH 08/49] Update src/routes/(console)/organization-[organization]/+page.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/routes/(console)/organization-[organization]/+page.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/+page.ts b/src/routes/(console)/organization-[organization]/+page.ts index ce972e3ba..047a2b965 100644 --- a/src/routes/(console)/organization-[organization]/+page.ts +++ b/src/routes/(console)/organization-[organization]/+page.ts @@ -18,9 +18,9 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) => const offset = pageToOffset(page, limit); const search = getSearch(url); - const archivedPage = parseInt(url.searchParams.get('archivedPage') || '1'); + 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: [ From c8bbb979fc46be1fe017de0538ec004bcb3b9025 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:06:10 +0530 Subject: [PATCH 09/49] lint issue --- src/routes/(console)/organization-[organization]/+page.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/(console)/organization-[organization]/+page.ts b/src/routes/(console)/organization-[organization]/+page.ts index 047a2b965..f4a520b5c 100644 --- a/src/routes/(console)/organization-[organization]/+page.ts +++ b/src/routes/(console)/organization-[organization]/+page.ts @@ -19,7 +19,8 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) => const search = getSearch(url); const archivedPageRaw = parseInt(url.searchParams.get('archivedPage') || '1', 10); - const archivedPage = Number.isFinite(archivedPageRaw) && archivedPageRaw > 0 ? archivedPageRaw : 1; + 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({ From c7cd36fd42bb10d4b5c9eeca1c723698b602ada1 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Thu, 30 Oct 2025 10:41:58 +0530 Subject: [PATCH 10/49] feaT: move github button above email-pass signup and signin --- .../(public)/(guest)/login/+page.svelte | 14 +++++------ .../(public)/(guest)/register/+page.svelte | 24 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/routes/(public)/(guest)/login/+page.svelte b/src/routes/(public)/(guest)/login/+page.svelte index e9f8f25b4..0127b8a2f 100644 --- a/src/routes/(public)/(guest)/login/+page.svelte +++ b/src/routes/(public)/(guest)/login/+page.svelte @@ -94,6 +94,13 @@
    + {#if isCloud} + + or + {/if} - {#if isCloud} - or - - {/if}
    diff --git a/src/routes/(public)/(guest)/register/+page.svelte b/src/routes/(public)/(guest)/register/+page.svelte index aafbbd2b6..ca7280d05 100644 --- a/src/routes/(public)/(guest)/register/+page.svelte +++ b/src/routes/(public)/(guest)/register/+page.svelte @@ -124,6 +124,18 @@
    + {#if isCloud} + + or + {/if} + . - - {#if isCloud} - or - - {/if}
    From 625abac21126b57901ad3ab0dfe5f04304c6208c Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Mon, 10 Nov 2025 18:16:40 +0000 Subject: [PATCH 11/49] added space below gitbutton --- src/routes/(public)/(guest)/login/+page.svelte | 13 ++++++++++++- src/routes/(public)/(guest)/register/+page.svelte | 7 +++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/routes/(public)/(guest)/login/+page.svelte b/src/routes/(public)/(guest)/login/+page.svelte index 0127b8a2f..396b60a18 100644 --- a/src/routes/(public)/(guest)/login/+page.svelte +++ b/src/routes/(public)/(guest)/login/+page.svelte @@ -95,7 +95,12 @@
    {#if isCloud} - @@ -129,3 +134,9 @@ + + diff --git a/src/routes/(public)/(guest)/register/+page.svelte b/src/routes/(public)/(guest)/register/+page.svelte index ca7280d05..d07a552e8 100644 --- a/src/routes/(public)/(guest)/register/+page.svelte +++ b/src/routes/(public)/(guest)/register/+page.svelte @@ -126,6 +126,7 @@ {#if isCloud} From 26f74312d7b44232affd2dc7def9f500f80e669e Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:23:12 +0530 Subject: [PATCH 13/49] onGithubLogin() to check for code or campaign parameters --- src/routes/(public)/(guest)/register/+page.svelte | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/routes/(public)/(guest)/register/+page.svelte b/src/routes/(public)/(guest)/register/+page.svelte index d64cd76b8..c5defbeb0 100644 --- a/src/routes/(public)/(guest)/register/+page.svelte +++ b/src/routes/(public)/(guest)/register/+page.svelte @@ -106,9 +106,17 @@ } function onGithubLogin() { + let successUrl = window.location.origin; + + if (page.url.searchParams.has('code')) { + successUrl += `?code=${page.url.searchParams.get('code')}`; + } else if (page.url.searchParams.has('campaign')) { + successUrl += `?campaign=${page.url.searchParams.get('campaign')}`; + } + sdk.forConsole.account.createOAuth2Session({ provider: OAuthProvider.Github, - success: window.location.origin, + success: successUrl, failure: window.location.origin, scopes: ['read:user', 'user:email'] }); From 8c514b76652ad195f19d91534cc3ed38ec4e43c1 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:36:11 +0530 Subject: [PATCH 14/49] addressed a comment --- src/routes/(public)/(guest)/login/+page.svelte | 17 +++++++---------- .../(public)/(guest)/register/+page.svelte | 17 +++++++---------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/routes/(public)/(guest)/login/+page.svelte b/src/routes/(public)/(guest)/login/+page.svelte index 396b60a18..698e32921 100644 --- a/src/routes/(public)/(guest)/login/+page.svelte +++ b/src/routes/(public)/(guest)/login/+page.svelte @@ -95,15 +95,12 @@ {#if isCloud} - +
    + +
    or {/if} diff --git a/src/routes/(public)/(guest)/register/+page.svelte b/src/routes/(public)/(guest)/register/+page.svelte index c5defbeb0..5a5de6525 100644 --- a/src/routes/(public)/(guest)/register/+page.svelte +++ b/src/routes/(public)/(guest)/register/+page.svelte @@ -133,15 +133,12 @@ {#if isCloud} - +
    + +
    or {/if} @@ -192,7 +189,7 @@ From a0a998789eabcdf8bef23e5423ade493f44ed728 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:44:53 +0530 Subject: [PATCH 15/49] inline styles --- src/routes/(public)/(guest)/login/+page.svelte | 8 +------- src/routes/(public)/(guest)/register/+page.svelte | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/routes/(public)/(guest)/login/+page.svelte b/src/routes/(public)/(guest)/login/+page.svelte index 698e32921..5d29042f4 100644 --- a/src/routes/(public)/(guest)/login/+page.svelte +++ b/src/routes/(public)/(guest)/login/+page.svelte @@ -95,7 +95,7 @@ {#if isCloud} -
    +
    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 b0d6bb469..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 @@ @@ -44,55 +49,53 @@ 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} -
    - - - - - {toLocaleDateTime(log.time)} - -
    - {/each} -
    -
    + + + + {log.event} + + + {#if log.countryCode !== '--'} + {log.countryName} + {:else} + Unknown + {/if} + + + + + + {toLocaleDateTime(log.time)} + + + {/each} + Date: Wed, 31 Dec 2025 00:31:51 +0530 Subject: [PATCH 22/49] sidebar was also not working on mobile view in pages under /account --- src/routes/(console)/+layout.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/(console)/+layout.svelte b/src/routes/(console)/+layout.svelte index 109bdabb3..d6231de58 100644 --- a/src/routes/(console)/+layout.svelte +++ b/src/routes/(console)/+layout.svelte @@ -334,7 +334,6 @@ Date: Wed, 31 Dec 2025 00:42:57 +0530 Subject: [PATCH 23/49] use pink column type --- src/lib/layout/activity.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/layout/activity.svelte b/src/lib/layout/activity.svelte index 6aef2a3aa..e7ce285a9 100644 --- a/src/lib/layout/activity.svelte +++ b/src/lib/layout/activity.svelte @@ -14,7 +14,7 @@ export let databasesScreen = false; export let useCreateLinkForPagination = true; - function getColumnWidth(columnId: string): { width?: number | { min: number } } { + function getColumnWidth(columnId: string): Pick { const widthConfig: Record< string, { insideSheet: number | { min: number }; default: number | { min: number } } From 1fa67227eda44ec24b076fd66be74475e8059941 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Wed, 31 Dec 2025 01:20:40 +0530 Subject: [PATCH 24/49] feat: New Group security cards --- .../auth/security/+page.svelte | 18 +-- .../auth/security/passwordPolicies.svelte | 150 ++++++++++++++++++ .../auth/security/sessionSecurity.svelte | 94 +++++++++++ .../security/updatePasswordDictionary.svelte | 61 ------- .../security/updatePasswordHistory.svelte | 80 ---------- .../security/updatePersonalDataCheck.svelte | 56 ------- .../auth/security/updateSessionAlerts.svelte | 54 ------- .../security/updateSessionInvalidation.svelte | 56 ------- 8 files changed, 250 insertions(+), 319 deletions(-) create mode 100644 src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte create mode 100644 src/routes/(console)/project-[region]-[project]/auth/security/sessionSecurity.svelte delete mode 100644 src/routes/(console)/project-[region]-[project]/auth/security/updatePasswordDictionary.svelte delete mode 100644 src/routes/(console)/project-[region]-[project]/auth/security/updatePasswordHistory.svelte delete mode 100644 src/routes/(console)/project-[region]-[project]/auth/security/updatePersonalDataCheck.svelte delete mode 100644 src/routes/(console)/project-[region]-[project]/auth/security/updateSessionAlerts.svelte delete mode 100644 src/routes/(console)/project-[region]-[project]/auth/security/updateSessionInvalidation.svelte 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..c7c34b5c1 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,20 @@ - - - - - + + 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..ddc4be087 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte @@ -0,0 +1,150 @@ + + + + + Password policies + + + + + + Enabling this option prevents users from reusing recent passwords by + comparing the new password with their password history. + + {#if passwordHistoryEnabled} + + {/if} + + + + + + + + Enabling this option prevent users from setting insecure passwords by + comparing the user's password with the 10k most commonly used passwords. + + + + + + + + Do not allow passwords that contain any part of the user's personal data. + This includes the user's name, email, or phone. + + + + + + + + + + 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..e2b4be9e0 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/auth/security/sessionSecurity.svelte @@ -0,0 +1,94 @@ + + +
    + + Session security + + + + + Enabling this option will send an email to the users when a new session is + created. + + + + + + + + Enabling this option will clear all existing sessions when the user changes + their password. + + + + + + + + + +
    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 @@ - - -
    - - Password dictionary - - - - Enabling this option prevent users from setting insecure passwords by comparing the - user's password with the 10k most commonly used passwords. - - - - - - - -
    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 @@ - - -
    - - Password history - Set the maximum number of passwords saved per user. - - - - Enabling this option prevents users from reusing recent passwords by comparing the - new password with their password history. - - - - - - - - -
    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 @@ - - -
    - - Personal data - - - - Do not allow passwords that contain any part of the user's personal data. This - includes the user's name, email, or phone. - - - - - - -
    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 @@ - - -
    - - Session alerts - - - - Enabling this option will send an email to the users when a new session is created. - - - - - - -
    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 @@ - - -
    - - Invalidate sessions - - - - Enabling this option will clear all existing sessions when the user changes their - password. - - - - - - - -
    From 44c693befdc39bfda382c1366cd06c88971a6ab9 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Wed, 31 Dec 2025 01:26:09 +0530 Subject: [PATCH 25/49] Update src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../auth/security/passwordPolicies.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte index ddc4be087..f39b5203d 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte @@ -117,7 +117,7 @@ label="Password dictionary"> - Enabling this option prevent users from setting insecure passwords by + Enabling this option prevents users from setting insecure passwords by comparing the user's password with the Date: Wed, 31 Dec 2025 10:37:18 +0530 Subject: [PATCH 26/49] no store import in security page --- .../auth/security/+page.svelte | 5 ++-- .../auth/security/passwordPolicies.svelte | 25 ++++++++++--------- .../auth/security/sessionSecurity.svelte | 5 ++-- 3 files changed, 18 insertions(+), 17 deletions(-) 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 c7c34b5c1..6c0ce4359 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/security/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/security/+page.svelte @@ -1,5 +1,6 @@ - - + + diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte index d57c73842..46547cecb 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte @@ -16,18 +16,29 @@ let passwordHistoryEnabled = $state(false); let passwordDictionary = $state(false); let authPersonalDataCheck = $state(false); + let lastValidLimit = $state(5); let maxPasswordInputField: InputNumber | null = null; // Initialize and sync state when project updates $effect(() => { const historyValue = project?.authPasswordHistory; - passwordHistory = historyValue < 1 ? 5 : historyValue; + if (historyValue && historyValue > 0) { + passwordHistory = historyValue; + lastValidLimit = historyValue; + } passwordHistoryEnabled = (historyValue ?? 0) !== 0; passwordDictionary = project?.authPasswordDictionary ?? false; authPersonalDataCheck = project?.authPersonalDataCheck ?? false; }); + // restore last valid limit when enabling + $effect(() => { + if (passwordHistoryEnabled && passwordHistory < 1) { + passwordHistory = lastValidLimit; + } + }); + $effect(() => { if (passwordHistoryEnabled) { tick().then(() => { From f6b561579e54f524cd4ebb4f436b7481c2c85165 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 2 Jan 2026 13:35:07 +0530 Subject: [PATCH 34/49] fix: race conditions for deletions. --- src/lib/actions/analytics.ts | 1 + src/lib/components/multiSelectTable.svelte | 104 ++++++++++++++---- .../auth/+page.svelte | 21 ++-- .../auth/teams/+page.svelte | 21 ++-- .../teams/team-[team]/members/+page.svelte | 23 ++-- .../auth/user-[user]/identities/table.svelte | 29 +++-- .../auth/user-[user]/memberships/+page.svelte | 17 +-- .../auth/user-[user]/targets/table.svelte | 29 +++-- .../database-[database]/backups/table.svelte | 58 +++++----- .../database-[database]/table.svelte | 24 ++-- .../executions/table.svelte | 23 ++-- .../function-[function]/table.svelte | 24 ++-- .../messaging/+page.svelte | 17 +-- .../messaging/providers/table.svelte | 29 +++-- .../messaging/topics/table.svelte | 29 +++-- .../topics/topic-[topic]/table.svelte | 27 +++-- .../overview/(components)/table.svelte | 2 +- .../overview/platforms/+page.svelte | 28 +++-- .../site-[site]/deployments/table.svelte | 24 ++-- .../sites/site-[site]/logs/table.svelte | 23 ++-- .../storage/bucket-[bucket]/+page.svelte | 23 ++-- 21 files changed, 371 insertions(+), 205 deletions(-) diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 6880f1c2f..d02f66731 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -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/multiSelectTable.svelte b/src/lib/components/multiSelectTable.svelte index cd35b38e6..bb985f790 100644 --- a/src/lib/components/multiSelectTable.svelte +++ b/src/lib/components/multiSelectTable.svelte @@ -1,5 +1,13 @@ {#key computeKey} @@ -104,13 +177,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 +196,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/routes/(console)/project-[region]-[project]/auth/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/+page.svelte index 391274650..e8b9c7a29 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/+page.svelte @@ -9,6 +9,7 @@ import { AvatarInitials, Copy, + type DeleteOperation, type DeleteOperationState, Empty, EmptySearch, @@ -60,20 +61,22 @@ ); } - async function handleDelete(selectedRows: string[]): Promise { - 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/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]/targets/table.svelte b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte index e71859a43..96414e558 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte @@ -1,5 +1,10 @@ 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.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]/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]/sites/site-[site]/deployments/table.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte index 690411c1e..d135498a6 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte +++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte @@ -1,5 +1,10 @@ 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]/storage/bucket-[bucket]/+page.svelte b/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte index 8caf3c05a..886169f11 100644 --- a/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte @@ -6,6 +6,7 @@ import { Avatar, type DeleteOperationState, + type DeleteOperation, Empty, EmptySearch, MultiSelectionTable, @@ -65,23 +66,25 @@ showDelete = true; } - async function handleBulkDelete(selectedRows: string[]): Promise { - const promises = selectedRows.map((fileId) => { - return sdk.forProject(page.params.region, page.params.project).storage.deleteFile({ + async function handleBulkDelete(batchDelete: DeleteOperation): Promise { + const result = await batchDelete((fileId) => + sdk.forProject(page.params.region, page.params.project).storage.deleteFile({ bucketId: page.params.bucket, fileId - }); - }); + }) + ); try { - await Promise.all(promises); - trackEvent(Submit.FileDelete, { total: selectedRows.length }); - } catch (error) { - trackError(error, Submit.FileDelete); - return error; + if (result.error) { + trackError(result.error, Submit.FileDelete); + } else { + trackEvent(Submit.FileDelete, { total: result.deleted.length }); + } } finally { await invalidate(Dependencies.FILES); } + + return result; } const beforeunload = (event: BeforeUnloadEvent) => { From 7a595b1197add536f835df75c2fa0505a84d8e7e Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 2 Jan 2026 14:21:37 +0530 Subject: [PATCH 35/49] update: improvements. --- .../auth/security/passwordPolicies.svelte | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte index 46547cecb..004e772ee 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte @@ -8,65 +8,63 @@ import { sdk } from '$lib/stores/sdk'; import { Typography, Link, Layout } from '@appwrite.io/pink-svelte'; import type { Models } from '@appwrite.io/console'; - import { tick } from 'svelte'; + import { onMount } from 'svelte'; - let { project }: { project: Models.Project } = $props(); + let { + project + }: { + project: Models.Project; + } = $props(); - let passwordHistory = $state(5); - let passwordHistoryEnabled = $state(false); - let passwordDictionary = $state(false); - let authPersonalDataCheck = $state(false); let lastValidLimit = $state(5); + let passwordHistory = $state(5); + let passwordDictionary = $state(false); + let passwordHistoryEnabled = $state(false); + let authPersonalDataCheck = $state(false); - let maxPasswordInputField: InputNumber | null = null; - - // Initialize and sync state when project updates - $effect(() => { - const historyValue = project?.authPasswordHistory; + onMount(() => { + // update initial states here in onMount. + const historyValue = project.authPasswordHistory; if (historyValue && historyValue > 0) { passwordHistory = historyValue; lastValidLimit = historyValue; } + passwordHistoryEnabled = (historyValue ?? 0) !== 0; - passwordDictionary = project?.authPasswordDictionary ?? false; - authPersonalDataCheck = project?.authPersonalDataCheck ?? false; + passwordDictionary = project.authPasswordDictionary ?? false; + authPersonalDataCheck = project.authPersonalDataCheck ?? false; }); - // restore last valid limit when enabling $effect(() => { + // restore last valid limit when enabling if (passwordHistoryEnabled && passwordHistory < 1) { passwordHistory = lastValidLimit; } }); - $effect(() => { - if (passwordHistoryEnabled) { - tick().then(() => { - if (maxPasswordInputField) { - maxPasswordInputField.addInputFocus(); - } - }); - } - }); + const hasChanges = $derived.by(() => { + const dictChanged = passwordDictionary !== project.authPasswordDictionary; + const dataCheckChanged = authPersonalDataCheck !== project.authPersonalDataCheck; + const historyChanged = passwordHistoryEnabled !== (project.authPasswordHistory !== 0); - const hasChanges = $derived( - passwordHistoryEnabled !== ((project?.authPasswordHistory ?? 0) !== 0) || - (passwordHistoryEnabled && passwordHistory !== (project?.authPasswordHistory ?? 0)) || - passwordDictionary !== (project?.authPasswordDictionary ?? false) || - authPersonalDataCheck !== (project?.authPersonalDataCheck ?? false) - ); + return historyChanged || dictChanged || dataCheckChanged; + }); async function updatePasswordPolicies() { try { - await sdk.forConsole.projects.updateAuthPasswordHistory({ + const projectSdk = sdk.forConsole.projects; + + await projectSdk.updateAuthPasswordHistory({ projectId: project.$id, limit: passwordHistoryEnabled ? passwordHistory : 0 }); - await sdk.forConsole.projects.updateAuthPasswordDictionary({ + + await projectSdk.updateAuthPasswordDictionary({ projectId: project.$id, enabled: passwordDictionary }); - await sdk.forConsole.projects.updatePersonalDataCheck({ + + await projectSdk.updatePersonalDataCheck({ projectId: project.$id, enabled: authPersonalDataCheck }); @@ -108,10 +106,10 @@ required max={20} min={1} - id="password-history" + autofocus label="Limit" + id="password-history" bind:value={passwordHistory} - bind:this={maxPasswordInputField} helper="Maximum 20 passwords." /> {/if} From 2cd22b98c342cf08e8c908635f2d44342b2e099d Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:21:54 +0530 Subject: [PATCH 36/49] merged conflicts resolved --- src/lib/layout/activity.svelte | 43 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/lib/layout/activity.svelte b/src/lib/layout/activity.svelte index 61aa1e041..c77fa3488 100644 --- a/src/lib/layout/activity.svelte +++ b/src/lib/layout/activity.svelte @@ -75,32 +75,31 @@
    {log.userName ?? 'Anonymous'} {/if} - - + + - - {log.event} - + + {log.event} + - - {#if log.countryCode !== '--'} - {log.countryName} - {:else} - Unknown - {/if} - + + {#if log.countryCode !== '--'} + {log.countryName} + {:else} + Unknown + {/if} + - - - + + + - - - - -{/each} - -
    + + + + + {/each} + Date: Fri, 2 Jan 2026 14:24:37 +0530 Subject: [PATCH 37/49] add: comment. --- src/lib/components/multiSelectTable.svelte | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/components/multiSelectTable.svelte b/src/lib/components/multiSelectTable.svelte index bb985f790..78e6b7388 100644 --- a/src/lib/components/multiSelectTable.svelte +++ b/src/lib/components/multiSelectTable.svelte @@ -50,6 +50,10 @@ deleteContentNotice?: Snippet; onDelete?: ( batchDelete: DeleteOperation, + /** + * this is useful if you have a custom deletion logic + * and don't want to use the batchDelete helper provided! + */ selectedRows: string[] ) => Promise | DeleteOperationState; onCancel?: () => Promise | void; From 7b74d4210c10e4ca5adb0f995f916991587554c5 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 2 Jan 2026 14:25:21 +0530 Subject: [PATCH 38/49] add: comment. --- src/lib/components/multiSelectTable.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/multiSelectTable.svelte b/src/lib/components/multiSelectTable.svelte index 78e6b7388..79197f2f7 100644 --- a/src/lib/components/multiSelectTable.svelte +++ b/src/lib/components/multiSelectTable.svelte @@ -51,8 +51,8 @@ onDelete?: ( batchDelete: DeleteOperation, /** - * this is useful if you have a custom deletion logic - * and don't want to use the batchDelete helper provided! + * this is useful when you have a custom deletion logic + * and the default `batchDelete` helper doesn't fit the use-case! */ selectedRows: string[] ) => Promise | DeleteOperationState; From 0c65c0d79060215e899a4d656bd035a651b51769 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Fri, 2 Jan 2026 15:02:07 +0530 Subject: [PATCH 39/49] added limitchanged --- .../auth/security/passwordPolicies.svelte | 12 ++++++++---- .../auth/security/sessionSecurity.svelte | 14 ++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte index 004e772ee..22898b060 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/security/passwordPolicies.svelte @@ -43,11 +43,15 @@ }); const hasChanges = $derived.by(() => { - const dictChanged = passwordDictionary !== project.authPasswordDictionary; - const dataCheckChanged = authPersonalDataCheck !== project.authPersonalDataCheck; - const historyChanged = passwordHistoryEnabled !== (project.authPasswordHistory !== 0); + const dictChanged = passwordDictionary !== (project.authPasswordDictionary ?? false); + const dataCheckChanged = authPersonalDataCheck !== (project.authPersonalDataCheck ?? false); + const historyChanged = + passwordHistoryEnabled !== ((project.authPasswordHistory ?? 0) !== 0); + const limitChanged = + passwordHistoryEnabled && + Number(passwordHistory) !== (project.authPasswordHistory ?? 0); - return historyChanged || dictChanged || dataCheckChanged; + return historyChanged || dictChanged || dataCheckChanged || limitChanged; }); async function updatePasswordPolicies() { diff --git a/src/routes/(console)/project-[region]-[project]/auth/security/sessionSecurity.svelte b/src/routes/(console)/project-[region]-[project]/auth/security/sessionSecurity.svelte index f64fee94e..6babbbecc 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/security/sessionSecurity.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/security/sessionSecurity.svelte @@ -8,22 +8,24 @@ import { sdk } from '$lib/stores/sdk'; import { Typography } from '@appwrite.io/pink-svelte'; import type { Models } from '@appwrite.io/console'; + import { onMount } from 'svelte'; let { project }: { project: Models.Project } = $props(); let authSessionAlerts = $state(false); let sessionInvalidation = $state(false); - // Initialize state from project - $effect(() => { + onMount(() => { authSessionAlerts = project?.authSessionAlerts ?? false; sessionInvalidation = project?.authInvalidateSessions ?? false; }); - const hasChanges = $derived( - authSessionAlerts !== (project?.authSessionAlerts ?? false) || - sessionInvalidation !== (project?.authInvalidateSessions ?? false) - ); + const hasChanges = $derived.by(() => { + const alertsChanged = authSessionAlerts !== (project?.authSessionAlerts ?? false); + const invalidationChanged = + sessionInvalidation !== (project?.authInvalidateSessions ?? false); + return alertsChanged || invalidationChanged; + }); async function updateSessionSecurity() { try { From 59900a4ca285534029101285993a91415995e196 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Fri, 2 Jan 2026 17:21:42 +0530 Subject: [PATCH 40/49] addressed code rabbit --- src/lib/components/limit.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/components/limit.svelte b/src/lib/components/limit.svelte index 8a7c1ef67..4221f7693 100644 --- a/src/lib/components/limit.svelte +++ b/src/lib/components/limit.svelte @@ -27,7 +27,9 @@ if (url.searchParams.has(pageParam)) { const page = Number(url.searchParams.get(pageParam)); - const newPage = Math.floor(((page - 1) * previousLimit) / limit); + 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); From f8a04a20520798d71a97b63364da2f6c0a6d37d8 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 2 Jan 2026 18:02:52 +0530 Subject: [PATCH 41/49] address comment. --- src/lib/components/multiSelectTable.svelte | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/components/multiSelectTable.svelte b/src/lib/components/multiSelectTable.svelte index 79197f2f7..ce0196e34 100644 --- a/src/lib/components/multiSelectTable.svelte +++ b/src/lib/components/multiSelectTable.svelte @@ -93,6 +93,11 @@ const deleted: string[] = []; let firstError: Error | undefined; + // prevent infinite loop + if (batchSize !== undefined) { + batchSize = Math.max(1, Math.floor(Math.abs(batchSize))); + } + async function processBatch(batch: string[]) { // build promises const results = await Promise.allSettled(batch.map((id) => deleteFn(id))); From 09884a935aae625d109afff4a837d1dbb709d0c7 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 2 Jan 2026 19:18:44 +0530 Subject: [PATCH 42/49] fix: payments not updating. --- .../account/payments/editPaymentModal.svelte | 46 +++++++++---------- .../organization-[organization]/+layout.ts | 15 ++---- .../billing/+page.svelte | 22 ++++----- .../billing/+page.ts | 37 +++++++++++++-- .../billing/deleteOrgPayment.svelte | 13 +++--- .../billing/paymentMethods.svelte | 43 +++++++---------- .../billing/replaceCard.svelte | 2 +- 7 files changed, 92 insertions(+), 86 deletions(-) diff --git a/src/routes/(console)/account/payments/editPaymentModal.svelte b/src/routes/(console)/account/payments/editPaymentModal.svelte index 7850cdea9..fab629996 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)/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]/billing/+page.svelte b/src/routes/(console)/organization-[organization]/billing/+page.svelte index 99729b8e9..ab69b151b 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'; @@ -26,18 +25,8 @@ 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 - ); - onMount(async () => { if (page.url.searchParams.has('type')) { if (page.url.searchParams.get('type') === 'upgrade') { @@ -115,7 +104,7 @@ {/if} {/if} - {#if defaultPaymentMethod?.failed && !backupPaymentMethod} + {#if data.primaryPaymentMethod?.failed && !data.backupPaymentMethod} @@ -146,7 +135,14 @@ 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,10 @@ 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 +103,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..7290ae055 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,11 @@ {: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..a02dde00b 100644 --- a/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte +++ b/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte @@ -73,7 +73,7 @@ type: 'success', message: `Your ${isBackup ? 'backup' : 'default'} payment method has been updated` }); - invalidate(Dependencies.ORGANIZATION); + await invalidate(Dependencies.PAYMENT_METHODS); trackEvent( isBackup ? Submit.OrganizationBackupPaymentDelete : Submit.OrganizationPaymentDelete ); From 4eda58bb999811b16869604c2eae5c2d849489ad Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 2 Jan 2026 19:28:44 +0530 Subject: [PATCH 43/49] improvements. --- .../account/payments/editPaymentModal.svelte | 4 +- .../billing/+page.svelte | 10 +-- .../billing/+page.ts | 5 +- .../billing/deleteOrgPayment.svelte | 3 +- .../billing/replaceCard.svelte | 68 ++++++++++--------- 5 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/routes/(console)/account/payments/editPaymentModal.svelte b/src/routes/(console)/account/payments/editPaymentModal.svelte index fab629996..24a8fa08f 100644 --- a/src/routes/(console)/account/payments/editPaymentModal.svelte +++ b/src/routes/(console)/account/payments/editPaymentModal.svelte @@ -12,12 +12,12 @@ let { show = $bindable(false), isLinked = false, - selectedPaymentMethod, + selectedPaymentMethod }: { show: boolean; isLinked?: boolean; selectedPaymentMethod: PaymentMethodData; - } = $props() + } = $props(); let year: number | null = $state(null); let month: string | null = $state(null); diff --git a/src/routes/(console)/organization-[organization]/billing/+page.svelte b/src/routes/(console)/organization-[organization]/billing/+page.svelte index ab69b151b..51d7fe353 100644 --- a/src/routes/(console)/organization-[organization]/billing/+page.svelte +++ b/src/routes/(console)/organization-[organization]/billing/+page.svelte @@ -20,12 +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; $: organization = data.organization; + $: baseUrl = resolve('/(console)/organization-[organization]/billing', { + organization: organization.$id + }); onMount(async () => { if (page.url.searchParams.has('type')) { @@ -47,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}` ); } @@ -140,8 +143,7 @@ methods={data?.paymentMethods} organization={data?.organization} backupMethod={data.backupPaymentMethod} - primaryMethod={data.primaryPaymentMethod} - /> + primaryMethod={data.primaryPaymentMethod} /> { // make number const credits = availableCredit ? availableCredit.available : null; - const { - backup, - primary - } = getOrganizationPaymentMethods(organization, paymentMethods) + const { backup, primary } = getOrganizationPaymentMethods(organization, paymentMethods); return { paymentMethods, diff --git a/src/routes/(console)/organization-[organization]/billing/deleteOrgPayment.svelte b/src/routes/(console)/organization-[organization]/billing/deleteOrgPayment.svelte index 7290ae055..1de90ac4a 100644 --- a/src/routes/(console)/organization-[organization]/billing/deleteOrgPayment.svelte +++ b/src/routes/(console)/organization-[organization]/billing/deleteOrgPayment.svelte @@ -68,8 +68,7 @@ bind:error bind:open={showDelete} title="Remove payment method" - onSubmit={isBackup ? removeBackupMethod : removeDefaultMethod} - > + 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/replaceCard.svelte b/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte index a02dde00b..b703f0842 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 = 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.OrganizationBackupPaymentDelete : Submit.OrganizationPaymentDelete + ); 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` }); - await invalidate(Dependencies.PAYMENT_METHODS); - 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.

    Date: Fri, 2 Jan 2026 19:34:37 +0530 Subject: [PATCH 44/49] fix: lint. --- .../organization-[organization]/billing/replaceCard.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte b/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte index b703f0842..4c326488a 100644 --- a/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte +++ b/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte @@ -14,7 +14,7 @@ import type { PaymentMethod } from '@stripe/stripe-js'; let { - show = false, + show = $bindable(false), isBackup = false, methods, organization From fdc94a584f1935faaba2dc3d807198732588e113 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 2 Jan 2026 19:36:01 +0530 Subject: [PATCH 45/49] fix: events. --- .../organization-[organization]/billing/replaceCard.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte b/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte index 4c326488a..920f7b474 100644 --- a/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte +++ b/src/routes/(console)/organization-[organization]/billing/replaceCard.svelte @@ -34,7 +34,7 @@ const filteredMethods = $derived(methods?.paymentMethods.filter((method) => !!method?.last4)); const submitEvent = $derived( - isBackup ? Submit.OrganizationBackupPaymentDelete : Submit.OrganizationPaymentDelete + isBackup ? Submit.OrganizationBackupPaymentUpdate : Submit.OrganizationPaymentUpdate ); onMount(async () => { From fb2d12d33c2cb97cae67f2a9fda217c7e75b8c89 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 10:36:53 -0800 Subject: [PATCH 46/49] Remove payment mandate setup functionality (#2748) * Initial plan * Remove all mandate-related code including setupPaymentMandate, checkForMandate, and paymentMandate component Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com> --- .../billing/alerts/paymentMandate.svelte | 26 ------------------- src/lib/sdk/billing.ts | 20 -------------- src/lib/stores/billing.ts | 21 +-------------- src/routes/(console)/+layout.svelte | 2 -- 4 files changed, 1 insertion(+), 68 deletions(-) delete mode 100644 src/lib/components/billing/alerts/paymentMandate.svelte 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. - - - - -{/if} diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index 11b5ba519..994506e72 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -1300,26 +1300,6 @@ export class Billing { ); } - async setupPaymentMandate( - organizationId: string, - paymentMethodId: string - ): Promise { - 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 d6231de58..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); From 3191f3d89e1f6b5c30fe2eaf726ffca9621ddb3c Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Sun, 4 Jan 2026 14:15:37 +0530 Subject: [PATCH 47/49] fix: improve mobile table display for domain verification --- src/lib/components/domains/cnameTable.svelte | 20 +++++++++----- src/lib/components/domains/recordTable.svelte | 26 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) 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 - @ - + @ + From 3d1f1632b7495b67dc78c12b300cc4e99062114f Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 5 Jan 2026 01:15:47 +0530 Subject: [PATCH 48/49] fix: added a tooltip over disabled create database button --- .../databases/+page.svelte | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) 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} - + +
    + +
    + + You have reached the maximum number of databases for your plan. + +
    {/if} From b721ab3109482c23704d8244574737914b97cdba Mon Sep 17 00:00:00 2001 From: Hemachandar <132386067+hmacr@users.noreply.github.com> Date: Mon, 5 Jan 2026 15:56:00 +0530 Subject: [PATCH 49/49] Show last checked time for domain verification (#2760) * Show last checked time for domain verification * feedback * better label --- src/lib/helpers/date.ts | 12 ++++++ .../function-[function]/domains/+page.ts | 1 + .../function-[function]/domains/store.ts | 7 ++++ .../function-[function]/domains/table.svelte | 32 ++++++++++++++++ .../settings/domains/+page.ts | 6 ++- .../settings/domains/table.svelte | 38 +++++++++++++++++++ .../sites/site-[site]/domains/+page.ts | 1 + .../sites/site-[site]/domains/store.ts | 7 ++++ .../sites/site-[site]/domains/table.svelte | 32 ++++++++++++++++ 9 files changed, 135 insertions(+), 1 deletion(-) 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/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]/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]/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}