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 and per-page limit for archived projects; archived and active lists show separate totals and navigation.
  • Improvements

    • More accurate counts in badges and cards for archived and active projects.
    • Customizable pagination query parameter with option to hide it on the first page.
    • UI tweaks: slightly shorter name truncation, adjusted platform display threshold, and improved pagination spacing and layout pass-through.

Copy link

appwrite bot commented Sep 2, 2025

Console

Project ID: 688b7bf400350cbd60e9

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

Note

You can use Avatars API to generate QR code for any text or URLs.

@HarshMN2345 HarshMN2345 self-assigned this Sep 2, 2025
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, and archived paging state (archivedPage, archivedOffset). The organization page uses these fields to bind lists, totals, and pagination; it passes archivedTotalOverall, archivedOffset, and limit into ArchiveProject. ArchiveProject now reads those props, shows archived totals, and renders PaginationWithLimit with pageParam="archivedPage" and removeOnFirstPage. Pagination, Limit, and PaginationWithLimit accept pageParam and removeOnFirstPage; paginator and paginationWithLimit forward extra props to their layout containers. Minor UI tweaks include name truncation and pagination spacing.

Possibly related PRs

Suggested reviewers

  • lohanidamodar
  • Meldiron

Pre-merge checks (2 passed, 1 inconclusive)

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description Check ❓ Inconclusive The current description consists mostly of template placeholders and a screenshot without any textual summary of the implemented changes, resulting in a vague explanation that does not convey meaningful details about the modifications. Please enhance the description by adding a concise summary of what was changed, details on how the pagination and archived pagination features work, a clear test plan demonstrating how to verify these changes, and any relevant related issues or PR references.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly summarizes the primary purpose of the pull request by indicating that it implements pagination for organization projects and adds archived project pagination, directly reflecting the main changes introduced.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

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

📒 Files selected for processing (2)
  • src/lib/components/paginationWithLimit.svelte (1 hunks)
  • src/lib/components/paginator.svelte (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/components/paginator.svelte
  • src/lib/components/paginationWithLimit.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). (2)
  • GitHub Check: e2e
  • GitHub Check: build
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-add-pagination-archive-project-sectiin

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 +28 to +36
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());
}
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 when recomputing page after limit change.

newPage needs a +1 to convert from 0-based index to 1-based page. Without it, unchanged limits incorrectly drop page N to page N-1, and scaling limits can jump to the wrong page.

Apply:

-        if (url.searchParams.has(pageParam)) {
-            const page = Number(url.searchParams.get(pageParam));
-            const newPage = Math.floor(((page - 1) * previousLimit) / limit);
-            const safePage = Math.max(1, Number.isFinite(newPage) ? newPage : 1);
+        if (url.searchParams.has(pageParam)) {
+            const page = Number(url.searchParams.get(pageParam));
+            const prev = Number.isFinite(previousLimit) && previousLimit > 0 ? previousLimit : limit;
+            const newPage = Math.floor(((page - 1) * prev) / limit) + 1;
+            const safePage = Math.max(1, Number.isFinite(newPage) ? newPage : 1);
             if (removeOnFirstPage && safePage === 1) {
                 url.searchParams.delete(pageParam);
             } else {
                 url.searchParams.set(pageParam, safePage.toString());
             }
         }
📝 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
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());
}
if (url.searchParams.has(pageParam)) {
const page = Number(url.searchParams.get(pageParam));
const prev = Number.isFinite(previousLimit) && previousLimit > 0 ? previousLimit : limit;
const newPage = Math.floor(((page - 1) * prev) / limit) + 1;
const safePage = Math.max(1, Number.isFinite(newPage) ? newPage : 1);
if (removeOnFirstPage && safePage === 1) {
url.searchParams.delete(pageParam);
} else {
url.searchParams.set(pageParam, safePage.toString());
}
}
🤖 Prompt for AI Agents
In src/lib/components/limit.svelte around lines 28 to 36, the recomputation of
newPage when adjusting limits uses a 0-based index but the app uses 1-based page
numbers; add +1 to the newPage calculation so it converts back to 1-based pages
(i.e., compute newPage = Math.floor(((page - 1) * previousLimit) / limit) + 1),
then keep the existing safePage logic and pageParam removal/setting unchanged.

Comment on lines +21 to +23
const archivedPage = parseInt(url.searchParams.get('archivedPage') || '1');
const archivedOffset = pageToOffset(archivedPage, limit);

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

Clamp and sanitize archivedPage to avoid negative/NaN offsets.

Negative pages produce invalid negative offsets.

-    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;
📝 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
const archivedPage = parseInt(url.searchParams.get('archivedPage') || '1');
const archivedOffset = pageToOffset(archivedPage, limit);
const archivedPageRaw = parseInt(url.searchParams.get('archivedPage') || '1', 10);
const archivedPage = Number.isFinite(archivedPageRaw) && archivedPageRaw > 0 ? archivedPageRaw : 1;
const archivedOffset = pageToOffset(archivedPage, limit);
🤖 Prompt for AI Agents
In src/routes/(console)/organization-[organization]/+page.ts around lines 21 to
23, the parsed archivedPage can be NaN or negative which leads to invalid
negative offsets; sanitize the value after parseInt by checking isNaN and
clamping to a minimum of 1 (e.g., if parsed is NaN or <1 set archivedPage = 1)
or alternatively compute archivedOffset as Math.max(0,
pageToOffset(sanitizedPage, limit)); update subsequent uses to rely on this
sanitized archivedPage/archivedOffset.

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.

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.

3 participants