Skip to content

Commit 3081141

Browse files
authored
Merge pull request #2312 from appwrite/feat-add-pagination-archive-project-sectiin
feat: org projects pagination and add archived pagination
2 parents 66d0d8d + 59900a4 commit 3081141

7 files changed

Lines changed: 144 additions & 39 deletions

File tree

src/lib/components/archiveProject.svelte

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script lang="ts">
2+
import PaginationWithLimit from './paginationWithLimit.svelte';
23
import { Button, InputText } from '$lib/elements/forms';
34
import { GridItem1, CardContainer, Modal } from '$lib/components';
45
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
@@ -45,9 +46,19 @@
4546
projectsToArchive: Models.Project[];
4647
organization: Organization;
4748
currentPlan: Plan;
49+
archivedTotalOverall: number;
50+
archivedOffset: number;
51+
limit: number;
4852
}
4953
50-
let { projectsToArchive, organization, currentPlan }: Props = $props();
54+
let {
55+
projectsToArchive,
56+
organization,
57+
currentPlan,
58+
archivedTotalOverall,
59+
archivedOffset,
60+
limit
61+
}: Props = $props();
5162
5263
// Check if current plan order is less than Pro (order < 1 means FREE plan)
5364
let isPlanBelowPro = $derived(currentPlan?.order < 1);
@@ -187,7 +198,7 @@
187198
}
188199
189200
import { formatName as formatNameHelper } from '$lib/helpers/string';
190-
function formatName(name: string, limit: number = 19) {
201+
function formatName(name: string, limit: number = 16) {
191202
return formatNameHelper(name, limit, $isSmallViewport);
192203
}
193204
</script>
@@ -196,7 +207,7 @@
196207
<div class="archive-projects-margin-top">
197208
<Accordion
198209
title={isPlanBelowPro ? 'Archived projects' : 'Pending archive'}
199-
badge={`${projectsToArchive.length}`}>
210+
badge={`${archivedTotalOverall}`}>
200211
<Typography.Text tag="p" size="s">
201212
{#if isPlanBelowPro}
202213
These projects are archived and require a plan upgrade to restore access.
@@ -206,7 +217,7 @@
206217
</Typography.Text>
207218

208219
<div class="archive-projects-margin">
209-
<CardContainer disableEmpty={true} total={projectsToArchive.length}>
220+
<CardContainer disableEmpty={true} total={archivedTotalOverall}>
210221
{#each projectsToArchive as project}
211222
{@const platforms = filterPlatforms(
212223
project.platforms.map((platform) => getPlatformInfo(platform.type))
@@ -266,7 +277,7 @@
266277
</Badge>
267278
{/each}
268279

269-
{#if platforms.length > 3}
280+
{#if platforms.length > 2}
270281
<Badge
271282
variant="secondary"
272283
content={`+${platforms.length - 2}`}
@@ -282,6 +293,15 @@
282293
</GridItem1>
283294
{/each}
284295
</CardContainer>
296+
297+
<PaginationWithLimit
298+
name="Archived Projects"
299+
{limit}
300+
offset={archivedOffset}
301+
total={archivedTotalOverall}
302+
pageParam="archivedPage"
303+
removeOnFirstPage
304+
class="pagination-container" />
285305
</div>
286306
</Accordion>
287307
</div>
@@ -355,4 +375,7 @@
355375
align-items: center;
356376
gap: 8px;
357377
}
378+
:global(.pagination-container) {
379+
margin-top: 16px;
380+
}
358381
</style>

src/lib/components/limit.svelte

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
export let sum: number;
99
export let limit: number;
1010
export let name: string;
11+
export let pageParam: string = 'page';
12+
export let removeOnFirstPage: boolean = false;
1113
1214
const options = [
1315
{ label: '6', value: 6 },
@@ -23,10 +25,17 @@
2325
url.searchParams.set('limit', limit.toString());
2426
await preferences.setLimit(limit);
2527
26-
if (url.searchParams.has('page')) {
27-
const page = Number(url.searchParams.get('page'));
28-
const newPage = Math.floor(((page - 1) * previousLimit) / limit);
29-
url.searchParams.set('page', newPage.toString());
28+
if (url.searchParams.has(pageParam)) {
29+
const page = Number(url.searchParams.get(pageParam));
30+
const prev =
31+
Number.isFinite(previousLimit) && previousLimit > 0 ? previousLimit : limit;
32+
const newPage = Math.floor(((page - 1) * prev) / limit) + 1;
33+
const safePage = Math.max(1, Number.isFinite(newPage) ? newPage : 1);
34+
if (removeOnFirstPage && safePage === 1) {
35+
url.searchParams.delete(pageParam);
36+
} else {
37+
url.searchParams.set(pageParam, safePage.toString());
38+
}
3039
}
3140
3241
await goto(url.toString());

src/lib/components/pagination.svelte

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,21 @@
66
export let limit: number;
77
export let offset: number;
88
export let useCreateLink = true;
9+
export let pageParam: string = 'page';
10+
export let removeOnFirstPage: boolean = false;
911
1012
$: currentPage = Math.floor(offset / limit + 1);
1113
1214
function getLink(page: number): string {
1315
const url = new URL(pageStore.url);
1416
if (page === 1) {
15-
url.searchParams.delete('page');
17+
if (removeOnFirstPage) {
18+
url.searchParams.delete(pageParam);
19+
} else {
20+
url.searchParams.set(pageParam, '1');
21+
}
1622
} else {
17-
url.searchParams.set('page', page.toString());
23+
url.searchParams.set(pageParam, page.toString());
1824
}
1925
2026
return url.toString();

src/lib/components/paginationWithLimit.svelte

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,37 @@
88
offset,
99
total,
1010
name,
11-
useCreateLink = true
11+
useCreateLink = true,
12+
pageParam = 'page',
13+
removeOnFirstPage = false,
14+
...restProps
1215
}: {
1316
limit: number;
1417
offset: number;
1518
total: number;
1619
name: string;
1720
useCreateLink?: boolean;
21+
pageParam?: string;
22+
removeOnFirstPage?: boolean;
23+
[key: string]: unknown;
1824
} = $props();
1925
2026
const showLimit = $derived(!!useCreateLink);
2127
const direction = $derived(showLimit ? 'row' : 'column');
2228
const alignItems = $derived(showLimit ? 'center' : 'flex-end');
2329
</script>
2430

25-
<Layout.Stack wrap="wrap" {direction} {alignItems} justifyContent="space-between">
31+
<Layout.Stack wrap="wrap" {direction} {alignItems} justifyContent="space-between" {...restProps}>
2632
{#if showLimit}
27-
<Limit {limit} sum={total} {name} />
33+
<Limit {limit} sum={total} {name} {pageParam} {removeOnFirstPage} />
2834
{/if}
2935

30-
<Pagination on:page {limit} {offset} sum={total} {useCreateLink} />
36+
<Pagination
37+
on:page
38+
{limit}
39+
{offset}
40+
sum={total}
41+
{useCreateLink}
42+
{pageParam}
43+
{removeOnFirstPage} />
3144
</Layout.Stack>

src/lib/components/paginator.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
name = 'items',
1414
gap = 's',
1515
offset = $bindable(0),
16-
children
16+
children,
17+
...restProps
1718
}: {
1819
items: T[];
1920
limit?: number;
@@ -26,14 +27,15 @@
2627
| undefined;
2728
offset?: number;
2829
children: Snippet<[T[], number]>;
30+
[key: string]: unknown;
2931
} = $props();
3032
3133
let total = $derived(items.length);
3234
3335
let paginatedItems = $derived(items.slice(offset, offset + limit));
3436
</script>
3537

36-
<Layout.Stack {gap}>
38+
<Layout.Stack {gap} {...restProps}>
3739
{@render children(paginatedItems, limit)}
3840

3941
{#if !hideFooter}

src/routes/(console)/organization-[organization]/+page.svelte

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,6 @@
7171
return getServiceLimit('projects', null, data.currentPlan);
7272
});
7373
74-
const projectsToArchive = $derived.by(() => {
75-
return isCloud
76-
? data.projects.projects.filter((project) => project.status === 'archived')
77-
: [];
78-
});
79-
8074
function filterPlatforms(platforms: { name: string; icon: string }[]) {
8175
return platforms.filter(
8276
(value, index, self) => index === self.findIndex((t) => t.name === value.name)
@@ -136,6 +130,19 @@
136130
return project.status === 'archived';
137131
}
138132
133+
const projectsToArchive = $derived(
134+
(data.archivedProjectsPage ?? data.projects.projects).filter(
135+
(project) => project.status === 'archived'
136+
)
137+
);
138+
139+
const activeTotalOverall = $derived(
140+
data?.activeTotalOverall ??
141+
data?.organization?.projects?.length ??
142+
data?.projects?.total ??
143+
0
144+
);
145+
139146
function clearSearch() {
140147
searchQuery?.clearInput();
141148
}
@@ -238,7 +245,7 @@
238245
{#if data.projects.total > 0}
239246
<CardContainer
240247
disableEmpty={!$canWriteProjects}
241-
total={data.projects.total}
248+
total={activeTotalOverall}
242249
offset={data.offset}
243250
on:click={handleCreateProject}>
244251
{#each data.projects.projects as project}
@@ -323,13 +330,16 @@
323330
name="Projects"
324331
limit={data.limit}
325332
offset={data.offset}
326-
total={data.projects.total} />
333+
total={activeTotalOverall} />
327334

328335
<!-- Archived Projects Section -->
329336
<ArchiveProject
330337
{projectsToArchive}
331338
organization={data.organization}
332-
currentPlan={$currentPlan} />
339+
currentPlan={$currentPlan}
340+
archivedTotalOverall={data.archivedTotalOverall}
341+
archivedOffset={data.archivedOffset}
342+
limit={data.limit} />
333343
</Container>
334344
<CreateOrganization bind:show={addOrganization} />
335345
<CreateProject bind:show={showCreate} teamId={page.params.organization} />

src/routes/(console)/organization-[organization]/+page.ts

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { CARD_LIMIT, Dependencies } from '$lib/constants';
55
import type { PageLoad } from './$types';
66
import { redirect } from '@sveltejs/kit';
77
import { base } from '$app/paths';
8-
import { isCloud } from '$lib/system';
98

109
export const load: PageLoad = async ({ params, url, route, depends, parent }) => {
1110
const { scopes } = await parent();
@@ -20,26 +19,69 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
2019
const offset = pageToOffset(page, limit);
2120
const search = getSearch(url);
2221

23-
const projects = await sdk.forConsole.projects.list({
24-
queries: [
25-
Query.offset(offset),
26-
Query.equal('teamId', params.organization),
27-
Query.limit(limit),
28-
Query.orderDesc(''),
29-
Query.select(['$id', 'name', 'platforms', 'region', ...(isCloud ? ['status'] : [])])
30-
],
31-
search: search || undefined
32-
});
22+
const archivedPageRaw = parseInt(url.searchParams.get('archivedPage') || '1', 10);
23+
const archivedPage =
24+
Number.isFinite(archivedPageRaw) && archivedPageRaw > 0 ? archivedPageRaw : 1;
25+
const archivedOffset = pageToOffset(archivedPage, limit);
26+
const [activeProjects, archivedProjects, activeTotal, archivedTotal] = await Promise.all([
27+
sdk.forConsole.projects.list({
28+
queries: [
29+
Query.offset(offset),
30+
Query.equal('teamId', params.organization),
31+
Query.or([Query.equal('status', 'active'), Query.isNull('status')]),
32+
Query.limit(limit),
33+
Query.orderDesc('')
34+
],
35+
search: search || undefined
36+
}),
37+
sdk.forConsole.projects.list({
38+
queries: [
39+
Query.offset(archivedOffset),
40+
Query.equal('teamId', params.organization),
41+
Query.equal('status', 'archived'),
42+
Query.limit(limit),
43+
Query.orderDesc('')
44+
],
45+
search: search || undefined
46+
}),
47+
sdk.forConsole.projects.list({
48+
queries: [
49+
Query.equal('teamId', params.organization),
50+
Query.or([Query.equal('status', 'active'), Query.isNull('status')])
51+
],
52+
search: search || undefined
53+
}),
54+
sdk.forConsole.projects.list({
55+
queries: [
56+
Query.equal('teamId', params.organization),
57+
Query.equal('status', 'archived')
58+
],
59+
search: search || undefined
60+
})
61+
]);
3362

3463
// set `default` if no region!
35-
for (const project of projects.projects) {
64+
for (const project of activeProjects.projects) {
65+
project.region ??= 'default';
66+
}
67+
for (const project of archivedProjects.projects) {
3668
project.region ??= 'default';
3769
}
3870

3971
return {
4072
offset,
4173
limit,
42-
projects,
74+
projects: {
75+
...activeProjects,
76+
projects: activeProjects.projects,
77+
total: activeTotal.total
78+
},
79+
activeProjectsPage: activeProjects.projects,
80+
archivedProjectsPage: archivedProjects.projects,
81+
activeTotalOverall: activeTotal.total,
82+
archivedTotalOverall: archivedTotal.total,
83+
archivedOffset,
84+
archivedPage,
4385
search
4486
};
4587
};

0 commit comments

Comments
 (0)