Skip to content

Conversation

@HarshMN2345
Copy link
Member

@HarshMN2345 HarshMN2345 commented Sep 2, 2025

What does this PR do?

image

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

yes

Summary by CodeRabbit

  • New Features

    • Independent pagination for archived projects with separate totals, offsets and per-page limits.
  • Improvements

    • Accurate totals reflected in badges and cards.
    • Customizable page query parameter with option to remove it on the first page.
    • UI tweaks: shorter name truncation, adjusted platform-badge threshold, improved pagination spacing.
  • Improvements (API surface)

    • Pagination and related components now accept and forward additional props for layout and passthrough.

✏️ Tip: You can customize this high-level summary in your review settings.

@appwrite
Copy link

appwrite bot commented Sep 2, 2025

Console (appwrite/console)

Project ID: 688b7bf400350cbd60e9

Sites (2)
Site Status Logs Preview QR
 console-stage
688b7cf6003b1842c9dc
Ready Ready View Logs Preview URL QR Code
 console-cloud
688b7c18002b9b871a8f
Ready Ready View Logs Preview URL QR Code

Tip

Storage files get ClamAV malware scanning and encryption by default

@HarshMN2345 HarshMN2345 self-assigned this Sep 2, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 2, 2025

Walkthrough

The load function now fetches active and archived projects separately and returns per-type pages, totals, archivedPage, and archivedOffset. The organization page uses active totals for CardContainer and PaginationWithLimit and passes archivedTotalOverall, archivedOffset, and limit into ArchiveProject. ArchiveProject added new props, tightened name truncation, adjusted platform-badge logic, replaced local counts with archivedTotalOverall, and renders PaginationWithLimit. Pagination, Limit, PaginationWithLimit, and Paginator accept/forward pageParam, removeOnFirstPage, and arbitrary extra props; global CSS for pagination spacing was added.

Possibly related PRs

Suggested reviewers

  • lohanidamodar
  • ItzNotABug
  • TorstenDittmann

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main changes: adding pagination for organization projects and implementing pagination for archived projects.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96bd510 and 59900a4.

📒 Files selected for processing (1)
  • src/lib/components/limit.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/components/limit.svelte
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@HarshMN2345 HarshMN2345 changed the title fix org projects pagination and add archived pagination feat: org projects pagination and add archived pagination Sep 2, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (10)
src/routes/(console)/organization-[organization]/+page.svelte (3)

107-109: Avoid redundant filtering when server already provides archived set

Use the pre-filtered data.archivedProjectsPage directly; fall back to filtering only when it’s absent.

-    $: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter(
-        (project) => project.status !== 'active'
-    );
+    $: projectsToArchive = data.archivedProjectsPage
+        ?? data.projects.projects.filter((project) => project.status !== 'active');

111-115: Prefer activeTotal fallback order and align all consumers to it

  • Reorder fallbacks so we prefer data.projects.total before data.organization.projects.length (the latter may include archived).
  • Also update any plan checks and props (e.g., Line 140 condition and Line 261 prop to CreateProjectCloud) to use activeTotalOverall for consistency.
-    $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
-        (project) => project.status === 'active'
-    );
-    $: activeTotalOverall = data?.activeTotalOverall ?? data?.organization?.projects?.length ?? data?.projects?.total ?? 0;
+    $: activeProjects = data.activeProjectsPage
+        ?? data.projects.projects.filter((project) => project.status === 'active');
+    $: activeTotalOverall =
+        data?.activeTotalOverall
+        ?? data?.projects?.total
+        ?? data?.organization?.projects?.length
+        ?? 0;

1-1: Fix Prettier issues

CI flagged a Prettier formatting warning for this file. Please run: pnpm prettier --write src/routes/(console)/organization-[organization]/+page.svelte.

src/lib/components/archiveProject.svelte (3)

149-151: Name truncation tightened to 16 chars — confirm UX parity

Active projects use 19/25 (small/regular). Dropping archived to 16 may create inconsistent card widths. Confirm with design or align to active logic.

-function formatName(name: string, limit: number = 16) {
+function formatName(name: string, limit: number = 19) { // or make it responsive like the active list

163-168: Paginator config: hide UI affordances when not needed and avoid magic numbers

  • With ≤6 items, you already hide the footer; consider also hiding page numbers.
  • Extract 6 into a local constant for clarity and future tuning.
-                <Paginator
-                    items={projectsToArchive}
-                    limit={6}
-                    hidePages={false}
-                    hideFooter={projectsToArchive.length <= 6}>
+                {#key projectsToArchive}<!-- reset page when data changes -->
+                <Paginator
+                    items={projectsToArchive}
+                    limit={ARCHIVE_PAGE_SIZE}
+                    hidePages={projectsToArchive.length <= ARCHIVE_PAGE_SIZE}
+                    hideFooter={projectsToArchive.length <= ARCHIVE_PAGE_SIZE}>
                     {#snippet children(items)}
                         <CardContainer disableEmpty={true} total={projectsToArchive.length}>
                             {#each items as project}
 ...
                         </CardContainer>
                     {/snippet}
                 </Paginator>

Add near the script top:

const ARCHIVE_PAGE_SIZE = 6;

Also applies to: 169-171, 264-267


1-1: Fix Prettier issues

CI flagged a Prettier formatting warning for this file. Please run: pnpm prettier --write src/lib/components/archiveProject.svelte.

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

56-56: Set region before deriving slices (minor)

Move region normalization before computing allActiveProjects/allArchivedProjects to keep derived arrays fully normalized. Low impact.

-    // set `default` if no region!
-    for (const project of allProjects) {
+    // set `default` if no region!
+    for (const project of allProjects) {
         project.region ??= 'default';
     }
-    const allActiveProjects = allProjects.filter((p) => p.status === 'active');
-    const allArchivedProjects = allProjects.filter((p) => p.status !== 'active');
+    const allActiveProjects = allProjects.filter((p) => p.status === 'active');
+    const allArchivedProjects = allProjects.filter((p) => p.status !== 'active');

31-48: Reduce N+1 API calls: decouple aggregation page size from UI page size

Looping with Query.limit(limit) ties server fetch size to UI page size. Use a larger server-side chunk (e.g., 100) within API limits to cut requests.

-    const limit = getLimit(url, route, CARD_LIMIT);
+    const limit = getLimit(url, route, CARD_LIMIT);
+    const AGG_LIMIT = Math.min(100, limit); // tune to SDK/API max page size
...
-                Query.limit(limit),
+                Query.limit(AGG_LIMIT),

1-1: Fix Prettier issues

CI flagged a Prettier formatting warning for this file. Please run: pnpm prettier --write src/routes/(console)/organization-[organization]/+page.ts.


37-42: Use explicit sort field or document default

Empty string in Query.orderDesc('') is used across the codebase to invoke the SDK’s default descending order (typically by creation date). For clarity, replace '' with an explicit field (e.g. '$createdAt') or add a comment noting that an empty string triggers default sorting.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1c0af8d and 2a058be.

📒 Files selected for processing (3)
  • src/lib/components/archiveProject.svelte (4 hunks)
  • src/routes/(console)/organization-[organization]/+page.svelte (3 hunks)
  • src/routes/(console)/organization-[organization]/+page.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/routes/(console)/organization-[organization]/+page.ts (2)
src/routes/(console)/organization-[organization]/store.ts (1)
  • projects (17-17)
src/lib/stores/sdk.ts (1)
  • sdk (142-165)
🪛 GitHub Actions: Tests
src/routes/(console)/organization-[organization]/+page.svelte

[warning] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix.

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

[warning] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix.

src/lib/components/archiveProject.svelte

[warning] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: e2e
🔇 Additional comments (5)
src/routes/(console)/organization-[organization]/+page.svelte (2)

165-165: LGTM: CardContainer now bound to active total

Using activeTotalOverall ensures paging/total reflects active projects only.


250-250: LGTM: Pagination total matches active total

PaginationWithLimit.total={activeTotalOverall} is correct and consistent with the active list.

src/lib/components/archiveProject.svelte (2)

3-3: LGTM: Paginator import

Importing Paginator from $lib/components is consistent with the component library usage.


258-261: Safe access on region already handled

Using region?.name avoids runtime errors when regions aren’t loaded yet. Good.

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

50-54: LGTM: Server-side split of active vs archived and per-page slice

This matches the UI’s new bindings and avoids filtering everything on the client.

Comment on lines 31 to 48
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;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Seed aggregation with the first response and avoid redundant page-0 fetch

You drop the first call’s results and refetch from offset 0, doubling cost and latency. Seed allProjects with the initial page and continue from there.

-    let allProjects: typeof projects.projects = [];
-    let fetchedCount = 0;
+    let allProjects: typeof projects.projects = projects.projects.slice();
+    let fetchedCount = allProjects.length;
     const total = projects.total;
 
-    while (fetchedCount < total) {
+    while (fetchedCount < total) {
         const next = await sdk.forConsole.projects.list({
             queries: [
-                Query.offset(fetchedCount),
+                Query.offset(fetchedCount),
                 Query.equal('teamId', params.organization),
-                Query.limit(limit),
+                Query.limit(limit),
                 Query.orderDesc('')
             ],
             search: search || undefined
         });
         allProjects = allProjects.concat(next.projects);
         fetchedCount += next.projects.length;
         if (next.projects.length === 0) break;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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;
}
// Seed with the initial page of projects instead of starting empty
let allProjects: typeof projects.projects = projects.projects.slice();
let fetchedCount = allProjects.length;
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;
}
🤖 Prompt for AI Agents
In src/routes/(console)/organization-[organization]/+page.ts around lines 31 to
48, the code currently performs an initial projects fetch but then starts the
loop that refetches from offset 0, doubling cost and latency; instead, perform
the first sdk.forConsole.projects.list call once, initialize allProjects with
that response and set fetchedCount to that response.projects.length, then enter
a loop that requests subsequent pages starting at the current fetchedCount
(using Query.offset(fetchedCount)) until fetchedCount >= total or a page returns
zero items; this removes the redundant page-0 fetch while preserving the same
paging logic.

Comment on lines 63 to 67
projects: { ...projects, projects: allProjects, total: allActiveProjects.length },
activeProjectsPage: activeProjectsForPage,
archivedProjectsPage: allArchivedProjects,
activeTotalOverall: allActiveProjects.length,
search
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Don’t mutate the projects contract; return aggregates separately

Overwriting projects.projects with all items and projects.total with “active total” breaks consumers expecting API semantics. Keep projects intact and expose new top-level fields for UI.

-        projects: { ...projects, projects: allProjects, total: allActiveProjects.length },
-        activeProjectsPage: activeProjectsForPage,
-        archivedProjectsPage: allArchivedProjects,
-        activeTotalOverall: allActiveProjects.length,
+        projects, // preserve API contract for stores and other consumers
+        activeProjectsPage: activeProjectsForPage,
+        archivedProjectsPage: allArchivedProjects,
+        activeTotalOverall: allActiveProjects.length,
+        // Optionally expose allProjects if needed elsewhere:
+        // allProjects,

🏁 Script executed:

#!/bin/bash
# Audit for consumers that assume `projects.total` is "all results" (not just active).
rg -nP --type=ts --type=svelte -C2 '\bprojects\.total\b'

Length of output: 89


🏁 Script executed:

#!/bin/bash
# Audit for consumers that assume `projects.total` remains the full count
rg -nP -g '*.ts' -g '*.svelte' -C2 '\bprojects\.total\b'

Length of output: 4397


Preserve projects API contract and expose aggregates separately
Overriding projects.projects and projects.total with active-only values breaks UI components that rely on the full project count (plan checks, creation modals, settings pages). Undo the mutation and introduce new top-level fields for the aggregates, for example:

-        projects: { ...projects, projects: allProjects, total: allActiveProjects.length },
+        projects, // preserve API contract
         activeProjectsPage: activeProjectsForPage,
         archivedProjectsPage: allArchivedProjects,
+        activeTotalOverall: allActiveProjects.length,
+        allProjects, // full list if needed

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/routes/(console)/organization-[organization]/+page.ts around lines 63 to
67, the code is mutating the existing projects object by replacing
projects.projects and projects.total with active-only values which breaks
consumers expecting the full projects API; revert that mutation so the original
projects object remains intact and add new top-level fields (e.g.,
activeProjectsPage and activeProjectsTotal or activeProjectsAggregate) to expose
active-only lists and counts, update any callers that need the active-only
values to use these new fields, and ensure the returned payload preserves the
original projects structure while exposing the aggregates separately.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/routes/(console)/organization-[organization]/+page.ts (2)

31-48: Avoid double-fetching and reduce total requests while aggregating.
You re-query from offset 0 after already fetching one page; on page 1 this duplicates page-0, and on later pages you still pay an extra request. Seed aggregation or restructure to a single aggregation pass.

Example approach (parallelized, larger page size):

-    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 total = projects.total;
+    const pageSize = Math.max(limit, 100); // fewer round-trips
+    const fetches: Promise<typeof projects>[] = [];
+    for (let start = 0; start < total; start += pageSize) {
+        fetches.push(
+            sdk.forConsole.projects.list({
+                queries: [
+                    Query.offset(start),
+                    Query.equal('teamId', params.organization),
+                    Query.limit(pageSize),
+                    Query.orderDesc('')
+                ],
+                search: search || undefined
+            })
+        );
+    }
+    const pages = await Promise.all(fetches);
+    let allProjects: typeof projects.projects = pages.flatMap((p) => p.projects);

63-66: Preserve the projects API contract; expose aggregates separately.
Overwriting projects.projects and projects.total breaks consumers that expect server semantics.

-        projects: { ...projects, projects: allProjects, total: allActiveProjects.length },
-        activeProjectsPage: activeProjectsForPage,
-        archivedProjectsPage: allArchivedProjects,
-        activeTotalOverall: allActiveProjects.length,
+        projects, // keep original paging contract
+        allProjects, // optional: full list for local consumers
+        activeProjectsPage: activeProjectsForPage,
+        archivedProjectsPage: allArchivedProjects,
+        activeTotalOverall: allActiveProjects.length,
🧹 Nitpick comments (4)
src/lib/components/archiveProject.svelte (2)

171-171: Key your each blocks for stable DOM reconciliation.
Use project id and platform name as keys.

-                            {#each items as project}
+                            {#each items as project (project.$id)}
...
-                                    {#each platforms.slice(0, 2) as platform}
+                                    {#each platforms.slice(0, 2) as platform (platform.name)}

Also applies to: 250-258


166-167: Nit: hidePages={false} is redundant if hideFooter hides controls.
You can remove hidePages unless it toggles other UI.

-                    hidePages={false}
                     hideFooter={projectsToArchive.length <= 6}>
src/routes/(console)/organization-[organization]/+page.ts (2)

56-58: Avoid mutating SDK objects in-place when normalizing region.
Safer to return normalized copies to prevent accidental shared-state mutations.

-    for (const project of allProjects) {
-        project.region ??= 'default';
-    }
+    allProjects = allProjects.map((p) => ({ ...p, region: p.region ?? 'default' }));

31-48: Optional: cap concurrency to balance latency and load.
If you parallelize page fetches, use a small pool (e.g., 4–6) rather than Promise.all on hundreds of pages.

If helpful, I can provide a pooled fetch helper.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2a058be and 82cc38d.

📒 Files selected for processing (3)
  • src/lib/components/archiveProject.svelte (3 hunks)
  • src/routes/(console)/organization-[organization]/+page.svelte (3 hunks)
  • src/routes/(console)/organization-[organization]/+page.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/routes/(console)/organization-[organization]/+page.svelte
🔇 Additional comments (3)
src/lib/components/archiveProject.svelte (2)

3-3: Good addition: local pagination wrapper imported.
Importing Paginator here aligns with the new archived pagination flow.


149-151: Name truncation tightened (19 → 16): verify UI doesn’t regress.
Please sanity-check long archived names across viewports for truncation/tooltip behavior.

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

53-54: Confirm page semantics when slicing only active projects.
offset is derived from all projects, but you slice allActiveProjects. If archived/active are interleaved, active pages may shift. Ensure the route’s page is intended to page active-only results.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/lib/components/archiveProject.svelte (1)

171-176: Guard against undefined platforms to avoid runtime errors.
This still calls .map on a possibly undefined project.platforms. Use a safe fallback.

-                                {@const platforms = filterPlatforms(
-                                    project.platforms.map((platform) =>
-                                        getPlatformInfo(platform.type)
-                                    )
-                                )}
+                                {@const platforms = filterPlatforms(
+                                    (project.platforms ?? []).map((platform) =>
+                                        getPlatformInfo(platform.type)
+                                    )
+                                )}
🧹 Nitpick comments (3)
src/lib/components/archiveProject.svelte (3)

149-151: Truncation default lowered to 16 — confirm UX consistency.
Verify this aligns with design across breakpoints; otherwise consider keeping the previous limit or making it a shared constant.


163-170: Paginator wiring and CardContainer total — confirm contract.

  • Ensure Paginator supports the {#snippet children(items)} API with items as the paged slice and that hideFooter behaves as intended.
  • If CardContainer’s total is meant to reflect items on the current page (not overall), switch to items.length to avoid UI/skeleton mismatches.

Proposed change if per-page is expected:

-                        <CardContainer disableEmpty={true} total={projectsToArchive.length}>
+                        <CardContainer disableEmpty={true} total={items.length}>

170-170: Key the each block for stable DOM when paginating.
Helps avoid unnecessary re-renders when page content changes.

-                            {#each items as project}
+                            {#each items as project (project.$id)}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 82cc38d and 11e460c.

📒 Files selected for processing (1)
  • src/lib/components/archiveProject.svelte (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: e2e
  • GitHub Check: build
🔇 Additional comments (3)
src/lib/components/archiveProject.svelte (3)

3-3: Importing Paginator for client-side pagination looks good.


260-265: Off-by-one fix for “+N” badge — LGTM.
Condition > 2 correctly shows “+1” when there are 3 platforms.


268-271: Region rendering gated safely — LGTM.
The $regionsStore?.regions check prevents .find on undefined and the UI handles missing names.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/routes/(console)/organization-[organization]/+page.ts (1)

73-77: Don’t repurpose projects for active-only; preserve API contract

Overwriting projects.total with the active count breaks consumers expecting the full “for this query” semantics (seen elsewhere in the app). Expose per-type fields separately as you already do.

-        projects: {
-            ...activeProjects,
-            projects: activeProjects.projects,
-            total: activeTotal.total
-        },
+        // Preserve original list response for the active query without altering semantics
+        projects: activeProjects,
         activeProjectsPage: activeProjects.projects,
         archivedProjectsPage: archivedProjects.projects,
         activeTotalOverall: activeTotal.total,
         archivedTotalOverall: archivedTotal.total,
src/lib/components/archiveProject.svelte (1)

218-221: Guard against undefined platforms array

.map on undefined will throw for projects missing platforms.

-                        {@const platforms = filterPlatforms(
-                            project.platforms.map((platform) => getPlatformInfo(platform.type))
-                        )}
+                        {@const platforms = filterPlatforms(
+                            (project.platforms ?? []).map((platform) => getPlatformInfo(platform.type))
+                        )}
🧹 Nitpick comments (3)
src/lib/components/archivedPagination.svelte (1)

7-7: Guard currentPage against limit=0/undefined

Avoid NaN/Infinity and clamp to page >= 1.

-    const currentPage = $derived(Math.floor(offset / limit + 1));
+    const currentPage = $derived(Math.max(1, Math.floor(offset / Math.max(1, Number(limit) || 1)) + 1));
src/lib/components/archivedPaginationWithLimit.svelte (1)

30-30: Drop unused event listener

on:page isn’t handled; remove to avoid confusion.

-    <ArchivedPagination on:page {limit} {offset} sum={total} />
+    <ArchivedPagination {limit} {offset} sum={total} />
src/routes/(console)/organization-[organization]/+page.ts (1)

21-23: Clamp archivedPage to ≥ 1

Protect against invalid/negative/zero values.

-    const archivedPage = parseInt(url.searchParams.get('archivedPage') || '1');
+    const archivedPage = Math.max(1, parseInt(url.searchParams.get('archivedPage') || '1', 10) || 1);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11e460c and 655b053.

📒 Files selected for processing (6)
  • src/lib/components/archiveProject.svelte (6 hunks)
  • src/lib/components/archivedLimit.svelte (1 hunks)
  • src/lib/components/archivedPagination.svelte (1 hunks)
  • src/lib/components/archivedPaginationWithLimit.svelte (1 hunks)
  • src/routes/(console)/organization-[organization]/+page.svelte (3 hunks)
  • src/routes/(console)/organization-[organization]/+page.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/routes/(console)/organization-[organization]/+page.ts (2)
src/lib/helpers/load.ts (1)
  • pageToOffset (4-6)
src/lib/stores/sdk.ts (1)
  • sdk (142-165)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: e2e

Comment on lines 18 to 36
async function limitChange() {
const url = new URL(pageStore.url);
const previousLimit = Number(url.searchParams.get('limit'));
url.searchParams.set('limit', limit.toString());
preferences.setLimit(limit);
// Handle archived page pagination
if (url.searchParams.has('archivedPage')) {
const page = Number(url.searchParams.get('archivedPage'));
const newPage = Math.floor(((page - 1) * previousLimit) / limit);
if (newPage === 1) {
url.searchParams.delete('archivedPage');
} else {
url.searchParams.set('archivedPage', newPage.toString());
}
}
await goto(url.toString());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix off-by-one and null handling when recalculating archivedPage on limit change

  • Off-by-one: should be floor(startIndex/newLimit) + 1.
  • When limit query is missing, Number(null) becomes 0 and can produce page 0.
  • Clamp to page >= 1 and defend against zero/invalid limits.
-        const previousLimit = Number(url.searchParams.get('limit'));
+        const previousLimit = Number(url.searchParams.get('limit') ?? limit) || limit;
@@
-        if (url.searchParams.has('archivedPage')) {
-            const page = Number(url.searchParams.get('archivedPage'));
-            const newPage = Math.floor(((page - 1) * previousLimit) / limit);
-            if (newPage === 1) {
+        if (url.searchParams.has('archivedPage')) {
+            const page = Math.max(1, Number(url.searchParams.get('archivedPage')) || 1);
+            const startIndex = (page - 1) * Math.max(1, previousLimit);
+            const newPage = Math.max(1, Math.floor(startIndex / Math.max(1, limit)) + 1);
+            if (newPage <= 1) {
                 url.searchParams.delete('archivedPage');
             } else {
-                url.searchParams.set('archivedPage', newPage.toString());
+                url.searchParams.set('archivedPage', String(newPage));
             }
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function limitChange() {
const url = new URL(pageStore.url);
const previousLimit = Number(url.searchParams.get('limit'));
url.searchParams.set('limit', limit.toString());
preferences.setLimit(limit);
// Handle archived page pagination
if (url.searchParams.has('archivedPage')) {
const page = Number(url.searchParams.get('archivedPage'));
const newPage = Math.floor(((page - 1) * previousLimit) / limit);
if (newPage === 1) {
url.searchParams.delete('archivedPage');
} else {
url.searchParams.set('archivedPage', newPage.toString());
}
}
await goto(url.toString());
}
async function limitChange() {
const url = new URL(pageStore.url);
const previousLimit = Number(url.searchParams.get('limit') ?? limit) || limit;
url.searchParams.set('limit', limit.toString());
preferences.setLimit(limit);
// Handle archived page pagination
if (url.searchParams.has('archivedPage')) {
const page = Math.max(1, Number(url.searchParams.get('archivedPage')) || 1);
const startIndex = (page - 1) * Math.max(1, previousLimit);
const newPage = Math.max(1, Math.floor(startIndex / Math.max(1, limit)) + 1);
if (newPage <= 1) {
url.searchParams.delete('archivedPage');
} else {
url.searchParams.set('archivedPage', String(newPage));
}
}
await goto(url.toString());
}

Comment on lines 112 to 118
$: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter(
(project) => (isCloud ? project.status === 'archived' : project.status !== 'active') // fallback for non-cloud
);
$: activeProjects = data.projects.projects.filter((project) => project.status !== 'archived');
$: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
(project) => project.status === 'active'
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct archived/active filters for non-cloud (null means active)

Per prior guidance, only status === 'archived' is archived; null is active. Current fallback misclassifies nulls.

-    $: 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) => project.status === 'archived'
+    );
@@
-    $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
-        (project) => project.status === 'active'
-    );
+    $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
+        (project) => project.status !== 'archived'
+    );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter(
(project) => (isCloud ? project.status === 'archived' : project.status !== 'active') // fallback for non-cloud
);
$: activeProjects = data.projects.projects.filter((project) => project.status !== 'archived');
$: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
(project) => project.status === 'active'
);
$: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter(
(project) => project.status === 'archived'
);
$: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
(project) => project.status !== 'archived'
);
🤖 Prompt for AI Agents
In src/routes/(console)/organization-[organization]/+page.svelte around lines
112–118, the fallback for non-cloud currently treats null as non-active causing
misclassification; change the archived filter to only include project.status ===
'archived' (no non-cloud fallback that treats null as archived), and change the
activeProjects filter to include project.status === 'active' or, for non-cloud,
treat null as active (i.e. project.status === 'active' || project.status == null
when isCloud is false).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/routes/(console)/organization-[organization]/+page.ts (1)

10-12: Use throw redirect(...), not return redirect(...).

SvelteKit expects a thrown redirect. Returning it won’t redirect.

-    if (!scopes.includes('projects.read') && scopes.includes('billing.read')) {
-        return redirect(301, `/console/organization-${params.organization}/billing`);
-    }
+    if (!scopes.includes('projects.read') && scopes.includes('billing.read')) {
+        throw redirect(301, `/console/organization-${params.organization}/billing`);
+    }
src/lib/components/archiveProject.svelte (1)

219-221: Guard against undefined platforms to prevent runtime crash

project.platforms can be undefined; .map would throw.

-                        {@const platforms = filterPlatforms(
-                            project.platforms.map((platform) => getPlatformInfo(platform.type))
-                        )}
+                        {@const platforms = filterPlatforms(
+                            (project.platforms ?? []).map((platform) =>
+                                getPlatformInfo(platform.type)
+                            )
+                        )}
♻️ Duplicate comments (2)
src/routes/(console)/organization-[organization]/+page.ts (1)

72-76: Preserve projects contract; don’t override with active-only total.

Overriding projects.total diverges from the SDK response shape and has broken consumers before. Keep projects as returned by the SDK and expose active/archived aggregates separately.

-        projects: {
-            ...activeProjects,
-            projects: activeProjects.projects,
-            total: activeTotal.total
-        },
+        projects: activeProjects, // preserve SDK response as-is

If callers need active total, use the already exposed activeTotalOverall.

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

116-118: Active filter should include null/undefined (treat anything not archived as active)

Self-hosted often returns no status for active items. Safer filter:

-    $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
-        (project) => project.status === 'active'
-    );
+    $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
+        (project) => project.status !== 'archived'
+    );
🧹 Nitpick comments (5)
src/lib/components/pagination.svelte (1)

17-24: Optional: sanitize target page input.

Defensive clamp avoids accidental 0/negative pages if upstream emits bad values.

-        if (page === 1) {
+        const p = Math.max(1, Math.floor(Number(page)));
+        if (p === 1) {
             if (removeOnFirstPage) {
                 url.searchParams.delete(pageParam);
             } else {
-                url.searchParams.set(pageParam, '1');
+                url.searchParams.set(pageParam, '1');
             }
         } else {
-            url.searchParams.set(pageParam, page.toString());
+            url.searchParams.set(pageParam, p.toString());
         }
src/routes/(console)/organization-[organization]/+page.ts (2)

24-59: Reduce payload for total-only queries.

Add a small limit to the total-only requests to avoid fetching full pages unnecessarily.

         sdk.forConsole.projects.list({
             queries: [
                 Query.equal('teamId', params.organization),
-                Query.or([Query.equal('status', 'active'), Query.isNull('status')])
+                Query.or([Query.equal('status', 'active'), Query.isNull('status')]),
+                Query.limit(1)
             ],
             search: search || undefined
         }),
         sdk.forConsole.projects.list({
             queries: [
                 Query.equal('teamId', params.organization),
-                Query.equal('status', 'archived')
+                Query.equal('status', 'archived'),
+                Query.limit(1)
             ],
             search: search || undefined
         })

If Appwrite totals are independent of limit, Query.limit(1) will still return correct total while reducing payload.


61-67: Mutation is fine, but consider immutability.

Setting project.region in-place is convenient but can surprise consumers if they rely on raw SDK objects. Consider mapping to new objects if immutability is preferred project-wide.

src/lib/components/archiveProject.svelte (2)

203-203: Name truncation default

Default 16 is fine; consider centralizing this constant to avoid drift with other views.


404-406: Pagination spacing style

Using :global is acceptable here; if PaginationWithLimit forwards classes, you could also keep it scoped by styling a local wrapper.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 655b053 and 4f4c679.

📒 Files selected for processing (7)
  • src/lib/components/archiveProject.svelte (6 hunks)
  • src/lib/components/limit.svelte (2 hunks)
  • src/lib/components/pagination.svelte (1 hunks)
  • src/lib/components/paginationWithLimit.svelte (1 hunks)
  • src/lib/components/paginator.svelte (2 hunks)
  • src/routes/(console)/organization-[organization]/+page.svelte (3 hunks)
  • src/routes/(console)/organization-[organization]/+page.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/routes/(console)/organization-[organization]/+page.ts (2)
src/lib/helpers/load.ts (1)
  • pageToOffset (4-6)
src/lib/stores/sdk.ts (1)
  • sdk (142-165)
🪛 ESLint
src/lib/components/paginationWithLimit.svelte

[error] 23-23: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/lib/components/paginator.svelte

[error] 30-30: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: e2e
  • GitHub Check: build
🔇 Additional comments (12)
src/lib/components/limit.svelte (1)

11-12: Good API extension with sensible defaults.

pageParam and removeOnFirstPage props look correct and align with the new archived pagination flow.

src/lib/components/pagination.svelte (1)

9-10: Prop plumbing for pageParam/removeOnFirstPage looks good.

Matches Limit/PaginationWithLimit and keeps default behavior intact.

src/lib/components/paginator.svelte (2)

16-18: Rest-props pass-through: OK, but ensure no accidental prop shadowing.

Spreading ...restProps after {gap} allows consumers to override gap if needed; just confirming this is intentional.


38-38: Prop forwarding is correct.

{...restProps} on Layout.Stack unlocks styling/attrs without changing behavior.

src/lib/components/paginationWithLimit.svelte (2)

11-14: New props are wired correctly through to children.

useCreateLink, pageParam, and removeOnFirstPage propagation is consistent with Pagination and Limit.


31-44: Wrapper wiring LGTM.

  • sum={total} matches Pagination API.
  • {pageParam} and {removeOnFirstPage} are correctly passed to both Limit and Pagination.
src/routes/(console)/organization-[organization]/+page.svelte (3)

112-114: Archived filter fix looks correct

Filtering strictly by project.status === 'archived' resolves the earlier misclassification.


120-124: Verify active totals propagate consistently to list and paginator

Using activeTotalOverall for both CardContainer and Pagination is correct. Please confirm server returns this as the full active count (not page size), so pagination math matches limit/offset.

Also applies to: 176-176, 261-261


267-270: Wiring archived pagination props to ArchiveProject

Props passed match the component’s new API.

src/lib/components/archiveProject.svelte (3)

52-55: Prop surface extension is coherent

New props (archivedTotalOverall, archivedOffset, limit) are clearly typed and destructured.

Also applies to: 57-64


2-2: Archived totals + pagination wiring

Import + usage of PaginationWithLimit and switching badges/totals to archivedTotalOverall look good. Please confirm:

  • PaginationWithLimit accepts pageParam and removeOnFirstPage.
  • It forwards $$restProps so class="pagination-container" applies to its root.
  • The loader reads archivedPage and computes archivedOffset accordingly.

Also applies to: 210-210, 217-217, 323-330


306-306: Off-by-one fix for “+N” badge

Condition > 2 is correct.

Comment on lines +24 to +59
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
})
]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Optional: verify Query.orderDesc('') usage.

Empty field name may be ignored or error depending on SDK version. Confirm intended default ordering or set an explicit field (e.g., createdAt/updatedAt).


🌐 Web query:

Is an empty string a valid field for Appwrite `Query.orderDesc('')`? What field should be used to order projects by most recently created in the Appwrite Console SDK?

💡 Result:

No — an empty string is not a valid field for Query.orderDesc(''). To sort by most-recently-created use the created timestamp field (Appwrite exposes $createdAt). Example: Query.orderDesc('$createdAt') — (escape the $ if your language/string parser requires, e.g. '$createdAt' in Flutter). You can also use $sequence for insertion order if preferred. [1][2][3]


Use explicit timestamp field in Query.orderDesc
Replace both instances of Query.orderDesc('') with Query.orderDesc('$createdAt') to ensure projects are sorted by most-recently-created timestamp.

🤖 Prompt for AI Agents
In src/routes/(console)/organization-[organization]/+page.ts around lines 24 to
59, the two calls using Query.orderDesc('') should explicitly sort by creation
timestamp; replace both Query.orderDesc('') occurrences with
Query.orderDesc('$createdAt') so projects are ordered by most-recently-created
items.

HarshMN2345 and others added 3 commits September 15, 2025 14:00
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/lib/components/archiveProject.svelte (1)

378-380: Global CSS for component-specific styling.

The .pagination-container class uses :global() to style the PaginationWithLimit component. While this works, consider whether this styling could be passed via a prop or if the component itself should handle this margin internally for better encapsulation.

Based on past review comments, ItzNotABug mentioned adding "restProp handling on the component so we can directly send custom styles instead of divs." This aligns with that suggestion—consider whether the class prop and global styling is the best approach, or if the spacing should be internal to the component.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ad7ee1 and 1934704.

📒 Files selected for processing (3)
  • src/lib/components/archiveProject.svelte
  • src/routes/(console)/organization-[organization]/+page.svelte
  • src/routes/(console)/organization-[organization]/+page.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/routes/(console)/organization-[organization]/+page.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/lib/components/archiveProject.svelte
  • src/routes/(console)/organization-[organization]/+page.svelte
src/lib/components/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for component file names and place them in src/lib/components/[feature]/ directory structure

Files:

  • src/lib/components/archiveProject.svelte
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/lib/components/archiveProject.svelte
  • src/routes/(console)/organization-[organization]/+page.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/lib/components/archiveProject.svelte
  • src/routes/(console)/organization-[organization]/+page.svelte
src/routes/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

Configure dynamic routes using SvelteKit convention with [param] syntax in route directory names

Files:

  • src/routes/(console)/organization-[organization]/+page.svelte
🧠 Learnings (1)
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to src/routes/**/*.svelte : Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Applied to files:

  • src/routes/(console)/organization-[organization]/+page.svelte
🪛 GitHub Actions: E2E
src/routes/(console)/organization-[organization]/+page.svelte

[error] 100-100: vite-plugin-svelte: legacy_reactive_statement_invalid - '$: is not allowed in runes mode, use $derived or $effect instead' at line 100.

🪛 GitHub Actions: Tests
src/routes/(console)/organization-[organization]/+page.svelte

[error] 133-133: $: is not allowed in runes mode; use $derived or $effect instead. (svelte) https://svelte.dev/e/legacy_reactive_statement_invalid


[error] 137-137: 'activeProjects' is declared but its value is never read. (ts)

🔇 Additional comments (7)
src/routes/(console)/organization-[organization]/+page.svelte (2)

249-249: LGTM: Consistent use of activeTotalOverall for pagination.

The bindings to activeTotalOverall correctly reflect the active project count across both the CardContainer and PaginationWithLimit components.

Also applies to: 334-334


340-343: LGTM: Archived pagination props correctly passed.

The new pagination props (archivedTotalOverall, archivedOffset, limit) are properly passed to the ArchiveProject component, enabling independent pagination for archived projects.

src/lib/components/archiveProject.svelte (5)

2-2: LGTM: Props correctly defined for archived pagination.

The component now accepts archivedTotalOverall, archivedOffset, and limit props, properly typed and destructured. This enables independent pagination for archived projects.

Also applies to: 49-61


210-210: LGTM: Total count correctly bound to archivedTotalOverall.

The Accordion badge and CardContainer total both use archivedTotalOverall instead of the local projectsToArchive.length, which correctly reflects the server-side total for pagination.

Also applies to: 220-220


280-285: LGTM: Platform badge condition corrected.

The condition platforms.length > 2 now correctly shows the "+N" badge when there are 3 or more platforms. This was addressed in a previous review.


297-304: LGTM: Pagination properly integrated with custom page parameter.

The PaginationWithLimit component uses pageParam="archivedPage" and removeOnFirstPage to maintain separate URL state for archived project pagination, preventing conflicts with the main project pagination.


201-203: Clarify the intentional difference in truncation limits for archived projects.

The formatName function in archiveProject.svelte uses a hardcoded limit of 16 characters, while the organization page applies viewport-aware limits of 19 (mobile) or 25 (desktop) for the same grid layout context. If this difference is intentional for the accordion design, document it with a comment; otherwise, align it with the parent page's truncation strategy.

Comment on lines 133 to 145
$: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter(
(project) => project.status === 'archived'
);
$: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
(project) => project.status === 'active'
);
$: activeTotalOverall =
data?.activeTotalOverall ??
data?.organization?.projects?.length ??
data?.projects?.total ??
0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Convert legacy reactive statements to $derived in runes mode.

The pipeline fails because $: is not allowed in Svelte 5 runes mode. Convert these reactive statements to $derived.

🔎 Proposed fix
-    $: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter(
-        (project) => project.status === 'archived'
-    );
-
-    $: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
-        (project) => project.status === 'active'
-    );
-
-    $: activeTotalOverall =
-        data?.activeTotalOverall ??
-        data?.organization?.projects?.length ??
-        data?.projects?.total ??
-        0;
+    const projectsToArchive = $derived(
+        (data.archivedProjectsPage ?? data.projects.projects).filter(
+            (project) => project.status === 'archived'
+        )
+    );
+
+    const activeProjects = $derived(
+        (data.activeProjectsPage ?? data.projects.projects).filter(
+            (project) => project.status === 'active'
+        )
+    );
+
+    const activeTotalOverall = $derived(
+        data?.activeTotalOverall ??
+        data?.organization?.projects?.length ??
+        data?.projects?.total ??
+        0
+    );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$: projectsToArchive = (data.archivedProjectsPage ?? data.projects.projects).filter(
(project) => project.status === 'archived'
);
$: activeProjects = (data.activeProjectsPage ?? data.projects.projects).filter(
(project) => project.status === 'active'
);
$: activeTotalOverall =
data?.activeTotalOverall ??
data?.organization?.projects?.length ??
data?.projects?.total ??
0;
const projectsToArchive = $derived(
(data.archivedProjectsPage ?? data.projects.projects).filter(
(project) => project.status === 'archived'
)
);
const activeProjects = $derived(
(data.activeProjectsPage ?? data.projects.projects).filter(
(project) => project.status === 'active'
)
);
const activeTotalOverall = $derived(
data?.activeTotalOverall ??
data?.organization?.projects?.length ??
data?.projects?.total ??
0
);
🧰 Tools
🪛 GitHub Actions: Tests

[error] 133-133: $: is not allowed in runes mode; use $derived or $effect instead. (svelte) https://svelte.dev/e/legacy_reactive_statement_invalid


[error] 137-137: 'activeProjects' is declared but its value is never read. (ts)

🤖 Prompt for AI Agents
In src/routes/(console)/organization-[organization]/+page.svelte around lines
133-145, replace the three legacy Svelte reactive statements with runes-mode
derived stores: import the runes $derived helper, then convert
projectsToArchive, activeProjects, and activeTotalOverall into $derived stores
that derive their values from the relevant source(s) (the page data store or
individual data properties) and compute the same filters/defaulting logic;
ensure the imports are updated and remove the `$:` statements so the computed
values are produced via $derived in runes mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants