Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
## 2024-05-18 - Concurrent Fetching with Owner IDs in Share Links
**Learning:** In the share system, endpoints processing a share code typically suffer from waterfall latency by first loading target entity details (like a project) and then querying its associated elements (like project lists) using the entity's owner ID (`userId`). However, the `shareLink` object already contains the `userId` field (representing the owner's ID).
**Action:** Always leverage the existing owner's ID within `shareLink` to bypass sequential dependencies and fetch parent entities (like projects) concurrently with their child elements (like user lists) using `Promise.all`.

## 2024-05-19 - [Redundant Frontend API Queries]
**Learning:** Fetching a global collection (e.g., all lists) and a subset of that collection (e.g., project lists) simultaneously on the frontend via separate API requests leads to duplicate backend database queries and unnecessary network overhead.
**Action:** When fetching a global collection and a subset on the frontend, derive the subset in-memory using array filtering instead of making a redundant API request.
20 changes: 9 additions & 11 deletions src/app/projects/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@ export default function ProjectDetailPage({ params }: { params: Promise<{ id: st
setIsLoading(true);

// OPTIMIZATION: Execute independent network requests and JSON parsing concurrently
// using Promise.all. This prevents a 3-step waterfall, reducing Time to First Byte
// (TTFB) and overall load time significantly on this detail page.
const [projectRes, listsRes, allListsRes] = await Promise.all([
// using Promise.all. This prevents a waterfall, reducing Time to First Byte (TTFB).
// Furthermore, since we fetch all lists for the project adding/editing UI, we can
// derive the project's specific lists in-memory rather than making a redundant
// /api/projects/[id]/lists API request, avoiding an unnecessary backend database query.
const [projectRes, allListsRes] = await Promise.all([
fetch(`/api/projects/${projectId}`),
fetch(`/api/projects/${projectId}/lists`),
fetch("/api/lists"),
]);

const [projectResult, listsResult, allListsResult] = await Promise.all([
const [projectResult, allListsResult] = await Promise.all([
projectRes.json(),
listsRes.json(),
allListsRes.json(),
]);

Expand All @@ -81,12 +81,10 @@ export default function ProjectDetailPage({ params }: { params: Promise<{ id: st
setEditName(projectResult.data.name);
setEditDescription(projectResult.data.description || "");

if (listsResult.success) {
setLists(listsResult.data);
}

if (allListsResult.success) {
setAllLists(allListsResult.data);
const listsData = allListsResult.data as List[];
setAllLists(listsData);
setLists(listsData.filter((list) => list.projectId === projectId));
}
Comment on lines 84 to 88
Comment on lines 84 to 88

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle failed /api/lists responses explicitly.

If allListsResult.success is false, the page currently renders with an empty lists state, which can incorrectly imply the project has no lists. Surface the error and clear list state deterministically.

Suggested fix
-      if (allListsResult.success) {
-        const listsData = allListsResult.data as List[];
-        setAllLists(listsData);
-        setLists(listsData.filter((list) => list.projectId === projectId));
-      }
+      if (!allListsResult.success) {
+        setAllLists([]);
+        setLists([]);
+        setError(allListsResult.error || "Failed to load lists");
+        return;
+      }
+
+      const listsData = allListsResult.data as List[];
+      setAllLists(listsData);
+      setLists(listsData.filter((list) => list.projectId === projectId));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/projects/`[id]/page.tsx around lines 84 - 88, When
allListsResult.success is false, explicitly clear and surface the failure
instead of leaving lists stale: in the component handling the API response
(page.tsx) update the branch around allListsResult to handle the else case by
calling setAllLists([]) and setLists([]) and also set or throw an error state
(e.g., setApiError or throw new Error(allListsResult.error || 'Failed to load
lists')) so the UI can render an error message; ensure you modify the block that
currently uses allListsResult, setAllLists and setLists so failures are
deterministic and visible to the user.

} catch (err) {
console.error("Error fetching project:", err);
Expand Down